• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <memory>
6 #include <string>
7 #include <utility>
8 #include <vector>
9 
10 #include "build/build_config.h"
11 #include "core/fpdfapi/font/cpdf_font.h"
12 #include "core/fpdfapi/page/cpdf_formobject.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_system.h"
21 #include "core/fxge/fx_font.h"
22 #include "fpdfsdk/cpdfsdk_helpers.h"
23 #include "public/cpp/fpdf_scopers.h"
24 #include "public/fpdf_annot.h"
25 #include "public/fpdf_edit.h"
26 #include "public/fpdfview.h"
27 #include "testing/embedder_test.h"
28 #include "testing/fx_string_testhelpers.h"
29 #include "testing/gmock/include/gmock/gmock-matchers.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "testing/utils/hash.h"
32 
33 class FPDFEditEmbedderTest : public EmbedderTest {
34  protected:
CreateNewDocument()35   FPDF_DOCUMENT CreateNewDocument() {
36     document_ = FPDF_CreateNewDocument();
37     cpdf_doc_ = CPDFDocumentFromFPDFDocument(document_);
38     return document_;
39   }
40 
CheckFontDescriptor(const CPDF_Dictionary * font_dict,int font_type,bool bold,bool italic,pdfium::span<const uint8_t> span)41   void CheckFontDescriptor(const CPDF_Dictionary* font_dict,
42                            int font_type,
43                            bool bold,
44                            bool italic,
45                            pdfium::span<const uint8_t> span) {
46     const CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor");
47     ASSERT_TRUE(font_desc);
48     EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type"));
49     EXPECT_EQ(font_dict->GetStringFor("BaseFont"),
50               font_desc->GetStringFor("FontName"));
51 
52     // Check that the font descriptor has the required keys according to spec
53     // 1.7 Table 5.19
54     ASSERT_TRUE(font_desc->KeyExist("Flags"));
55 
56     int font_flags = font_desc->GetIntegerFor("Flags");
57     EXPECT_EQ(bold, FontStyleIsForceBold(font_flags));
58     EXPECT_EQ(italic, FontStyleIsItalic(font_flags));
59     EXPECT_TRUE(FontStyleIsNonSymbolic(font_flags));
60     ASSERT_TRUE(font_desc->KeyExist("FontBBox"));
61 
62     const CPDF_Array* fontBBox = font_desc->GetArrayFor("FontBBox");
63     ASSERT_TRUE(fontBBox);
64     EXPECT_EQ(4u, fontBBox->size());
65     // Check that the coordinates are in the preferred order according to spec
66     // 1.7 Section 3.8.4
67     EXPECT_TRUE(fontBBox->GetIntegerAt(0) < fontBBox->GetIntegerAt(2));
68     EXPECT_TRUE(fontBBox->GetIntegerAt(1) < fontBBox->GetIntegerAt(3));
69 
70     EXPECT_TRUE(font_desc->KeyExist("ItalicAngle"));
71     EXPECT_TRUE(font_desc->KeyExist("Ascent"));
72     EXPECT_TRUE(font_desc->KeyExist("Descent"));
73     EXPECT_TRUE(font_desc->KeyExist("CapHeight"));
74     EXPECT_TRUE(font_desc->KeyExist("StemV"));
75     ByteString present("FontFile");
76     ByteString absent("FontFile2");
77     if (font_type == FPDF_FONT_TRUETYPE)
78       std::swap(present, absent);
79     EXPECT_TRUE(font_desc->KeyExist(present));
80     EXPECT_FALSE(font_desc->KeyExist(absent));
81 
82     auto streamAcc =
83         pdfium::MakeRetain<CPDF_StreamAcc>(font_desc->GetStreamFor(present));
84     streamAcc->LoadAllDataRaw();
85 
86     // Check that the font stream is the one that was provided
87     ASSERT_EQ(span.size(), streamAcc->GetSize());
88     if (font_type == FPDF_FONT_TRUETYPE) {
89       ASSERT_EQ(static_cast<int>(span.size()),
90                 streamAcc->GetDict()->GetIntegerFor("Length1"));
91     }
92 
93     const uint8_t* stream_data = streamAcc->GetData();
94     for (size_t j = 0; j < span.size(); j++)
95       EXPECT_EQ(span[j], stream_data[j]) << " at byte " << j;
96   }
97 
CheckCompositeFontWidths(const CPDF_Array * widths_array,CPDF_Font * typed_font)98   void CheckCompositeFontWidths(const CPDF_Array* widths_array,
99                                 CPDF_Font* typed_font) {
100     // Check that W array is in a format that conforms to PDF spec 1.7 section
101     // "Glyph Metrics in CIDFonts" (these checks are not
102     // implementation-specific).
103     EXPECT_GT(widths_array->size(), 1u);
104     int num_cids_checked = 0;
105     int cur_cid = 0;
106     for (size_t idx = 0; idx < widths_array->size(); idx++) {
107       int cid = widths_array->GetNumberAt(idx);
108       EXPECT_GE(cid, cur_cid);
109       ASSERT_FALSE(++idx == widths_array->size());
110       const CPDF_Object* next = widths_array->GetObjectAt(idx);
111       if (next->IsArray()) {
112         // We are in the c [w1 w2 ...] case
113         const CPDF_Array* arr = next->AsArray();
114         int cnt = static_cast<int>(arr->size());
115         size_t inner_idx = 0;
116         for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) {
117           uint32_t width = arr->GetNumberAt(inner_idx++);
118           EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid))
119               << " at cid " << cur_cid;
120         }
121         num_cids_checked += cnt;
122         continue;
123       }
124       // Otherwise, are in the c_first c_last w case.
125       ASSERT_TRUE(next->IsNumber());
126       int last_cid = next->AsNumber()->GetInteger();
127       ASSERT_FALSE(++idx == widths_array->size());
128       uint32_t width = widths_array->GetNumberAt(idx);
129       for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) {
130         EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid))
131             << " at cid " << cur_cid;
132       }
133       num_cids_checked += last_cid - cid + 1;
134     }
135     // Make sure we have a good amount of cids described
136     EXPECT_GT(num_cids_checked, 900);
137   }
cpdf_doc()138   CPDF_Document* cpdf_doc() { return cpdf_doc_; }
139 
140  private:
141   CPDF_Document* cpdf_doc_;
142 };
143 
144 namespace {
145 
146 const char kExpectedPDF[] =
147     "%PDF-1.7\r\n"
148     "%\xA1\xB3\xC5\xD7\r\n"
149     "1 0 obj\r\n"
150     "<</Pages 2 0 R /Type/Catalog>>\r\n"
151     "endobj\r\n"
152     "2 0 obj\r\n"
153     "<</Count 1/Kids\\[ 4 0 R \\]/Type/Pages>>\r\n"
154     "endobj\r\n"
155     "3 0 obj\r\n"
156     "<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
157     "endobj\r\n"
158     "4 0 obj\r\n"
159     "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R "
160     "/Resources<</ExtGState<</FXE1 5 0 R >>>>"
161     "/Rotate 0/Type/Page"
162     ">>\r\n"
163     "endobj\r\n"
164     "5 0 obj\r\n"
165     "<</BM/Normal/CA 1/ca 1>>\r\n"
166     "endobj\r\n"
167     "xref\r\n"
168     "0 6\r\n"
169     "0000000000 65535 f\r\n"
170     "0000000017 00000 n\r\n"
171     "0000000066 00000 n\r\n"
172     "0000000122 00000 n\r\n"
173     "0000000192 00000 n\r\n"
174     "0000000311 00000 n\r\n"
175     "trailer\r\n"
176     "<<\r\n"
177     "/Root 1 0 R\r\n"
178     "/Info 3 0 R\r\n"
179     "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
180     "startxref\r\n"
181     "354\r\n"
182     "%%EOF\r\n";
183 
184 }  // namespace
185 
TEST_F(FPDFEditEmbedderTest,EmptyCreation)186 TEST_F(FPDFEditEmbedderTest, EmptyCreation) {
187   EXPECT_TRUE(CreateEmptyDocument());
188   FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
189   EXPECT_NE(nullptr, page);
190   // The FPDFPage_GenerateContent call should do nothing.
191   EXPECT_TRUE(FPDFPage_GenerateContent(page));
192   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
193 
194   EXPECT_THAT(GetString(), testing::MatchesRegex(std::string(
195                                kExpectedPDF, sizeof(kExpectedPDF))));
196   FPDF_ClosePage(page);
197 }
198 
199 // Regression test for https://crbug.com/667012
TEST_F(FPDFEditEmbedderTest,RasterizePDF)200 TEST_F(FPDFEditEmbedderTest, RasterizePDF) {
201   const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
202 
203   // Get the bitmap for the original document.
204   ScopedFPDFBitmap orig_bitmap;
205   {
206     EXPECT_TRUE(OpenDocument("black.pdf"));
207     FPDF_PAGE orig_page = LoadPage(0);
208     ASSERT_TRUE(orig_page);
209     orig_bitmap = RenderLoadedPage(orig_page);
210     CompareBitmap(orig_bitmap.get(), 612, 792, kAllBlackMd5sum);
211     UnloadPage(orig_page);
212   }
213 
214   // Create a new document from |orig_bitmap| and save it.
215   {
216     FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
217     FPDF_PAGE temp_page = FPDFPage_New(temp_doc, 0, 612, 792);
218 
219     // Add the bitmap to an image object and add the image object to the output
220     // page.
221     FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);
222     EXPECT_TRUE(
223         FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap.get()));
224     EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
225     FPDFPage_InsertObject(temp_page, temp_img);
226     EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
227     EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
228     FPDF_ClosePage(temp_page);
229     FPDF_CloseDocument(temp_doc);
230   }
231 
232   // Get the generated content. Make sure it is at least as big as the original
233   // PDF.
234   EXPECT_GT(GetString().size(), 923u);
235   VerifySavedDocument(612, 792, kAllBlackMd5sum);
236 }
237 
238 // TODO(crbug.com/pdfium/11): Fix this test and enable.
239 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
240 #define MAYBE_AddPaths DISABLED_AddPaths
241 #else
242 #define MAYBE_AddPaths AddPaths
243 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_AddPaths)244 TEST_F(FPDFEditEmbedderTest, MAYBE_AddPaths) {
245   // Start with a blank page
246   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
247   ASSERT_TRUE(page);
248 
249   // We will first add a red rectangle
250   FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
251   ASSERT_TRUE(red_rect);
252   // Expect false when trying to set colors out of range
253   EXPECT_FALSE(FPDFPageObj_SetStrokeColor(red_rect, 100, 100, 100, 300));
254   EXPECT_FALSE(FPDFPageObj_SetFillColor(red_rect, 200, 256, 200, 0));
255 
256   // Fill rectangle with red and insert to the page
257   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
258   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
259 
260   int fillmode = FPDF_FILLMODE_NONE;
261   FPDF_BOOL stroke = true;
262   EXPECT_TRUE(FPDFPath_GetDrawMode(red_rect, &fillmode, &stroke));
263   EXPECT_EQ(FPDF_FILLMODE_ALTERNATE, fillmode);
264   EXPECT_FALSE(stroke);
265 
266   static const FS_MATRIX kMatrix = {1, 2, 3, 4, 5, 6};
267   EXPECT_FALSE(FPDFPath_SetMatrix(nullptr, &kMatrix));
268   EXPECT_TRUE(FPDFPath_SetMatrix(red_rect, &kMatrix));
269 
270   FS_MATRIX matrix;
271   EXPECT_FALSE(FPDFPath_GetMatrix(nullptr, &matrix));
272   EXPECT_TRUE(FPDFPath_GetMatrix(red_rect, &matrix));
273   EXPECT_FLOAT_EQ(1.0f, matrix.a);
274   EXPECT_FLOAT_EQ(2.0f, matrix.b);
275   EXPECT_FLOAT_EQ(3.0f, matrix.c);
276   EXPECT_FLOAT_EQ(4.0f, matrix.d);
277   EXPECT_FLOAT_EQ(5.0f, matrix.e);
278   EXPECT_FLOAT_EQ(6.0f, matrix.f);
279 
280   // Set back the identity matrix.
281   matrix = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
282   EXPECT_TRUE(FPDFPath_SetMatrix(red_rect, &matrix));
283 
284   FPDFPage_InsertObject(page, red_rect);
285   {
286     ScopedFPDFBitmap page_bitmap = RenderPage(page);
287     CompareBitmap(page_bitmap.get(), 612, 792,
288                   "66d02eaa6181e2c069ce2ea99beda497");
289   }
290 
291   // Now add to that a green rectangle with some medium alpha
292   FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
293   EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect, 0, 255, 0, 128));
294 
295   // Make sure the type of the rectangle is a path.
296   EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(green_rect));
297 
298   // Make sure we get back the same color we set previously.
299   unsigned int R;
300   unsigned int G;
301   unsigned int B;
302   unsigned int A;
303   EXPECT_TRUE(FPDFPageObj_GetFillColor(green_rect, &R, &G, &B, &A));
304   EXPECT_EQ(0u, R);
305   EXPECT_EQ(255u, G);
306   EXPECT_EQ(0u, B);
307   EXPECT_EQ(128u, A);
308 
309   // Make sure the path has 5 points (1 FXPT_TYPE::MoveTo and 4
310   // FXPT_TYPE::LineTo).
311   ASSERT_EQ(5, FPDFPath_CountSegments(green_rect));
312   // Verify actual coordinates.
313   FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(green_rect, 0);
314   float x;
315   float y;
316   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
317   EXPECT_EQ(100, x);
318   EXPECT_EQ(100, y);
319   EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
320   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
321   segment = FPDFPath_GetPathSegment(green_rect, 1);
322   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
323   EXPECT_EQ(100, x);
324   EXPECT_EQ(140, y);
325   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
326   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
327   segment = FPDFPath_GetPathSegment(green_rect, 2);
328   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
329   EXPECT_EQ(140, x);
330   EXPECT_EQ(140, y);
331   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
332   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
333   segment = FPDFPath_GetPathSegment(green_rect, 3);
334   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
335   EXPECT_EQ(140, x);
336   EXPECT_EQ(100, y);
337   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
338   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
339   segment = FPDFPath_GetPathSegment(green_rect, 4);
340   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
341   EXPECT_EQ(100, x);
342   EXPECT_EQ(100, y);
343   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
344   EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
345 
346   EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
347   FPDFPage_InsertObject(page, green_rect);
348   {
349     ScopedFPDFBitmap page_bitmap = RenderPage(page);
350     CompareBitmap(page_bitmap.get(), 612, 792,
351                   "7b0b87604594e773add528fae567a558");
352   }
353 
354   // Add a black triangle.
355   FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
356   EXPECT_TRUE(FPDFPageObj_SetFillColor(black_path, 0, 0, 0, 200));
357   EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
358   EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
359   EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
360   EXPECT_TRUE(FPDFPath_Close(black_path));
361 
362   // Make sure the path has 3 points (1 FXPT_TYPE::MoveTo and 2
363   // FXPT_TYPE::LineTo).
364   ASSERT_EQ(3, FPDFPath_CountSegments(black_path));
365   // Verify actual coordinates.
366   segment = FPDFPath_GetPathSegment(black_path, 0);
367   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
368   EXPECT_EQ(400, x);
369   EXPECT_EQ(100, y);
370   EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
371   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
372   segment = FPDFPath_GetPathSegment(black_path, 1);
373   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
374   EXPECT_EQ(400, x);
375   EXPECT_EQ(200, y);
376   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
377   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
378   segment = FPDFPath_GetPathSegment(black_path, 2);
379   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
380   EXPECT_EQ(300, x);
381   EXPECT_EQ(100, y);
382   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
383   EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
384   // Make sure out of bounds index access fails properly.
385   EXPECT_EQ(nullptr, FPDFPath_GetPathSegment(black_path, 3));
386 
387   FPDFPage_InsertObject(page, black_path);
388   {
389     ScopedFPDFBitmap page_bitmap = RenderPage(page);
390     CompareBitmap(page_bitmap.get(), 612, 792,
391                   "eadc8020a14dfcf091da2688733d8806");
392   }
393 
394   // Now add a more complex blue path.
395   FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
396   EXPECT_TRUE(FPDFPageObj_SetFillColor(blue_path, 0, 0, 255, 255));
397   EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
398   EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
399   EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
400   EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
401   EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
402   EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
403   EXPECT_TRUE(FPDFPath_Close(blue_path));
404   FPDFPage_InsertObject(page, blue_path);
405   const char kLastMD5[] = "9823e1a21bd9b72b6a442ba4f12af946";
406   {
407     ScopedFPDFBitmap page_bitmap = RenderPage(page);
408     CompareBitmap(page_bitmap.get(), 612, 792, kLastMD5);
409   }
410 
411   // Now save the result, closing the page and document
412   EXPECT_TRUE(FPDFPage_GenerateContent(page));
413   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
414   FPDF_ClosePage(page);
415 
416   // Render the saved result
417   VerifySavedDocument(612, 792, kLastMD5);
418 }
419 
TEST_F(FPDFEditEmbedderTest,ClipPath)420 TEST_F(FPDFEditEmbedderTest, ClipPath) {
421   // Load document with a clipped rectangle.
422   EXPECT_TRUE(OpenDocument("clip_path.pdf"));
423   FPDF_PAGE page = LoadPage(0);
424   ASSERT_TRUE(page);
425 
426   ASSERT_EQ(1, FPDFPage_CountObjects(page));
427 
428   FPDF_PAGEOBJECT triangle = FPDFPage_GetObject(page, 0);
429   ASSERT_TRUE(triangle);
430 
431   // Test that we got the expected triangle.
432   ASSERT_EQ(4, FPDFPath_CountSegments(triangle));
433 
434   FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(triangle, 0);
435   float x;
436   float y;
437   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
438   EXPECT_EQ(10, x);
439   EXPECT_EQ(10, y);
440   EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
441   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
442 
443   segment = FPDFPath_GetPathSegment(triangle, 1);
444   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
445   EXPECT_EQ(25, x);
446   EXPECT_EQ(40, y);
447   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
448   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
449 
450   segment = FPDFPath_GetPathSegment(triangle, 2);
451   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
452   EXPECT_EQ(40, x);
453   EXPECT_EQ(10, y);
454   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
455   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
456 
457   segment = FPDFPath_GetPathSegment(triangle, 3);
458   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
459   EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
460 
461   // Test FPDFPageObj_GetClipPath().
462   ASSERT_EQ(nullptr, FPDFPageObj_GetClipPath(nullptr));
463 
464   FPDF_CLIPPATH clip_path = FPDFPageObj_GetClipPath(triangle);
465   ASSERT_TRUE(clip_path);
466 
467   // Test FPDFClipPath_CountPaths().
468   ASSERT_EQ(-1, FPDFClipPath_CountPaths(nullptr));
469   ASSERT_EQ(1, FPDFClipPath_CountPaths(clip_path));
470 
471   // Test FPDFClipPath_CountPathSegments().
472   ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(nullptr, 0));
473   ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, -1));
474   ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, 1));
475   ASSERT_EQ(4, FPDFClipPath_CountPathSegments(clip_path, 0));
476 
477   // FPDFClipPath_GetPathSegment() negative testing.
478   ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(nullptr, 0, 0));
479   ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, -1, 0));
480   ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 1, 0));
481   ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 0, -1));
482   ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 0, 4));
483 
484   // FPDFClipPath_GetPathSegment() positive testing.
485   segment = FPDFClipPath_GetPathSegment(clip_path, 0, 0);
486   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
487   EXPECT_EQ(10, x);
488   EXPECT_EQ(15, y);
489   EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
490   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
491 
492   segment = FPDFClipPath_GetPathSegment(clip_path, 0, 1);
493   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
494   EXPECT_EQ(40, x);
495   EXPECT_EQ(15, y);
496   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
497   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
498 
499   segment = FPDFClipPath_GetPathSegment(clip_path, 0, 2);
500   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
501   EXPECT_EQ(40, x);
502   EXPECT_EQ(35, y);
503   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
504   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
505 
506   segment = FPDFClipPath_GetPathSegment(clip_path, 0, 3);
507   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
508   EXPECT_EQ(10, x);
509   EXPECT_EQ(35, y);
510   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
511   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
512 
513   UnloadPage(page);
514 }
515 
TEST_F(FPDFEditEmbedderTest,BUG_1399)516 TEST_F(FPDFEditEmbedderTest, BUG_1399) {
517   // Load document with a clipped rectangle.
518   EXPECT_TRUE(OpenDocument("bug_1399.pdf"));
519   FPDF_PAGE page = LoadPage(0);
520   ASSERT_TRUE(page);
521 
522   ASSERT_EQ(7, FPDFPage_CountObjects(page));
523 
524   FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
525   ASSERT_TRUE(obj);
526 
527   ASSERT_EQ(2, FPDFPath_CountSegments(obj));
528 
529   FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(obj, 0);
530   float x;
531   float y;
532   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
533   EXPECT_FLOAT_EQ(107.718f, x);
534   EXPECT_FLOAT_EQ(719.922f, y);
535   EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
536   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
537 
538   segment = FPDFPath_GetPathSegment(obj, 1);
539   EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
540   EXPECT_FLOAT_EQ(394.718f, x);
541   EXPECT_FLOAT_EQ(719.922f, y);
542   EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
543   EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
544 
545   FPDF_CLIPPATH clip_path = FPDFPageObj_GetClipPath(obj);
546   ASSERT_TRUE(clip_path);
547 
548   EXPECT_EQ(-1, FPDFClipPath_CountPaths(clip_path));
549   EXPECT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, 0));
550   EXPECT_FALSE(FPDFClipPath_GetPathSegment(clip_path, 0, 0));
551 
552   UnloadPage(page);
553 }
554 
555 // TODO(crbug.com/pdfium/11): Fix this test and enable.
556 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
557 #define MAYBE_SetText DISABLED_SetText
558 #else
559 #define MAYBE_SetText SetText
560 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_SetText)561 TEST_F(FPDFEditEmbedderTest, MAYBE_SetText) {
562   // Load document with some text.
563   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
564   FPDF_PAGE page = LoadPage(0);
565   ASSERT_TRUE(page);
566 
567   // Get the "Hello, world!" text object and change it.
568   ASSERT_EQ(2, FPDFPage_CountObjects(page));
569   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
570   ASSERT_TRUE(page_object);
571   ScopedFPDFWideString text1 = GetFPDFWideString(L"Changed for SetText test");
572   EXPECT_TRUE(FPDFText_SetText(page_object, text1.get()));
573 
574   // Verify the "Hello, world!" text is gone and "Changed for SetText test" is
575   // now displayed.
576   ASSERT_EQ(2, FPDFPage_CountObjects(page));
577 #if defined(OS_MACOSX)
578   const char kChangedMD5[] = "94c1e7a5af7dd9d77dc2223b1091acb7";
579 #elif defined(OS_WIN)
580   const char kChangedMD5[] = "3137fdb27962671f5c3963a5e965eff5";
581 #else
582   const char kChangedMD5[] = "a0c4ea6620772991f66bf7130379b08a";
583 #endif
584   {
585     ScopedFPDFBitmap page_bitmap = RenderPage(page);
586     CompareBitmap(page_bitmap.get(), 200, 200, kChangedMD5);
587   }
588 
589   // Now save the result.
590   EXPECT_TRUE(FPDFPage_GenerateContent(page));
591   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
592 
593   UnloadPage(page);
594 
595   // Re-open the file and check the changes were kept in the saved .pdf.
596   ASSERT_TRUE(OpenSavedDocument());
597   FPDF_PAGE saved_page = LoadSavedPage(0);
598   EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
599   {
600     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
601     CompareBitmap(page_bitmap.get(), 200, 200, kChangedMD5);
602   }
603 
604   CloseSavedPage(saved_page);
605   CloseSavedDocument();
606 }
607 
608 // TODO(crbug.com/pdfium/11): Fix this test and enable.
609 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
610 #define MAYBE_RemovePageObject DISABLED_RemovePageObject
611 #else
612 #define MAYBE_RemovePageObject RemovePageObject
613 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_RemovePageObject)614 TEST_F(FPDFEditEmbedderTest, MAYBE_RemovePageObject) {
615   // Load document with some text.
616   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
617   FPDF_PAGE page = LoadPage(0);
618   ASSERT_TRUE(page);
619 
620   // Show what the original file looks like.
621   {
622 #if defined(OS_MACOSX)
623     const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
624 #elif defined(OS_WIN)
625     const char kOriginalMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
626 #else
627     const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
628 #endif
629     ScopedFPDFBitmap page_bitmap = RenderPage(page);
630     CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
631   }
632 
633   // Get the "Hello, world!" text object and remove it.
634   ASSERT_EQ(2, FPDFPage_CountObjects(page));
635   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
636   ASSERT_TRUE(page_object);
637   EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
638 
639   // Verify the "Hello, world!" text is gone.
640   {
641 #if defined(OS_MACOSX)
642     const char kRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa";
643 #elif defined(OS_WIN)
644     const char kRemovedMD5[] = "aae6c5334721f90ec30d3d59f4ef7deb";
645 #else
646     const char kRemovedMD5[] = "b76df015fe88009c3c342395df96abf1";
647 #endif
648     ScopedFPDFBitmap page_bitmap = RenderPage(page);
649     CompareBitmap(page_bitmap.get(), 200, 200, kRemovedMD5);
650   }
651   ASSERT_EQ(1, FPDFPage_CountObjects(page));
652 
653   UnloadPage(page);
654   FPDFPageObj_Destroy(page_object);
655 }
656 
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)657 void CheckMarkCounts(FPDF_PAGE page,
658                      int start_from,
659                      int expected_object_count,
660                      size_t expected_prime_count,
661                      size_t expected_square_count,
662                      size_t expected_greater_than_ten_count,
663                      size_t expected_bounds_count) {
664   int object_count = FPDFPage_CountObjects(page);
665   ASSERT_EQ(expected_object_count, object_count);
666 
667   size_t prime_count = 0;
668   size_t square_count = 0;
669   size_t greater_than_ten_count = 0;
670   size_t bounds_count = 0;
671   for (int i = 0; i < object_count; ++i) {
672     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
673 
674     int mark_count = FPDFPageObj_CountMarks(page_object);
675     for (int j = 0; j < mark_count; ++j) {
676       FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
677 
678       char buffer[256];
679       unsigned long name_len = 999u;
680       ASSERT_TRUE(
681           FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
682       EXPECT_GT(name_len, 0u);
683       EXPECT_NE(999u, name_len);
684       std::wstring name =
685           GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
686       if (name == L"Prime") {
687         prime_count++;
688       } else if (name == L"Square") {
689         square_count++;
690         int expected_square = start_from + i;
691         EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
692 
693         unsigned long get_param_key_return = 999u;
694         ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer),
695                                                 &get_param_key_return));
696         EXPECT_EQ((6u + 1u) * 2u, get_param_key_return);
697         std::wstring key =
698             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
699         EXPECT_EQ(L"Factor", key);
700 
701         EXPECT_EQ(FPDF_OBJECT_NUMBER,
702                   FPDFPageObjMark_GetParamValueType(mark, "Factor"));
703         int square_root;
704         EXPECT_TRUE(
705             FPDFPageObjMark_GetParamIntValue(mark, "Factor", &square_root));
706         EXPECT_EQ(expected_square, square_root * square_root);
707       } else if (name == L"GreaterThanTen") {
708         greater_than_ten_count++;
709       } else if (name == L"Bounds") {
710         bounds_count++;
711         EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
712 
713         unsigned long get_param_key_return = 999u;
714         ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer),
715                                                 &get_param_key_return));
716         EXPECT_EQ((8u + 1u) * 2u, get_param_key_return);
717         std::wstring key =
718             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
719         EXPECT_EQ(L"Position", key);
720 
721         EXPECT_EQ(FPDF_OBJECT_STRING,
722                   FPDFPageObjMark_GetParamValueType(mark, "Position"));
723         unsigned long length;
724         EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
725             mark, "Position", buffer, sizeof(buffer), &length));
726         ASSERT_GT(length, 0u);
727         std::wstring value =
728             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
729 
730         // "Position" can be "First", "Last", or "End".
731         if (i == 0) {
732           EXPECT_EQ((5u + 1u) * 2u, length);
733           EXPECT_EQ(L"First", value);
734         } else if (i == object_count - 1) {
735           if (length == (4u + 1u) * 2u) {
736             EXPECT_EQ(L"Last", value);
737           } else if (length == (3u + 1u) * 2u) {
738             EXPECT_EQ(L"End", value);
739           } else {
740             FAIL();
741           }
742         } else {
743           FAIL();
744         }
745       } else {
746         FAIL();
747       }
748     }
749   }
750 
751   // Expect certain number of tagged objects. The test file contains strings
752   // from 1 to 19.
753   EXPECT_EQ(expected_prime_count, prime_count);
754   EXPECT_EQ(expected_square_count, square_count);
755   EXPECT_EQ(expected_greater_than_ten_count, greater_than_ten_count);
756   EXPECT_EQ(expected_bounds_count, bounds_count);
757 }
758 
TEST_F(FPDFEditEmbedderTest,ReadMarkedObjectsIndirectDict)759 TEST_F(FPDFEditEmbedderTest, ReadMarkedObjectsIndirectDict) {
760   // Load document with some text marked with an indirect property.
761   EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
762   FPDF_PAGE page = LoadPage(0);
763   ASSERT_TRUE(page);
764 
765   CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
766 
767   UnloadPage(page);
768 }
769 
770 // TODO(crbug.com/pdfium/11): Fix this test and enable.
771 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
772 #define MAYBE_RemoveMarkedObjectsPrime DISABLED_RemoveMarkedObjectsPrime
773 #else
774 #define MAYBE_RemoveMarkedObjectsPrime RemoveMarkedObjectsPrime
775 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_RemoveMarkedObjectsPrime)776 TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveMarkedObjectsPrime) {
777   // Load document with some text.
778   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
779   FPDF_PAGE page = LoadPage(0);
780   ASSERT_TRUE(page);
781 
782   // Show what the original file looks like.
783   {
784 #if defined(OS_MACOSX)
785     const char kOriginalMD5[] = "5a5eb63cb21cc15084fea1f14284b8df";
786 #elif defined(OS_WIN)
787     const char kOriginalMD5[] = "00542ee435b37749c4453be63bf7bdb6";
788 #else
789     const char kOriginalMD5[] = "41647268d5911d049801803b15c2dfb0";
790 #endif
791     ScopedFPDFBitmap page_bitmap = RenderPage(page);
792     CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
793   }
794 
795   constexpr int expected_object_count = 19;
796   CheckMarkCounts(page, 1, expected_object_count, 8, 4, 9, 1);
797 
798   // Get all objects marked with "Prime"
799   std::vector<FPDF_PAGEOBJECT> primes;
800   for (int i = 0; i < expected_object_count; ++i) {
801     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
802 
803     int mark_count = FPDFPageObj_CountMarks(page_object);
804     for (int j = 0; j < mark_count; ++j) {
805       FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
806 
807       char buffer[256];
808       unsigned long name_len = 999u;
809       ASSERT_TRUE(
810           FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
811       EXPECT_GT(name_len, 0u);
812       EXPECT_NE(999u, name_len);
813       std::wstring name =
814           GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
815       if (name == L"Prime") {
816         primes.push_back(page_object);
817       }
818     }
819   }
820 
821   // Remove all objects marked with "Prime".
822   for (FPDF_PAGEOBJECT page_object : primes) {
823     EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
824     FPDFPageObj_Destroy(page_object);
825   }
826 
827   EXPECT_EQ(11, FPDFPage_CountObjects(page));
828 
829 #if defined(OS_MACOSX)
830   const char kNonPrimesMD5[] = "57e76dc7375d896704f0fd6d6d1b9e65";
831   const char kNonPrimesAfterSaveMD5[] = "6304512d0150bbd5578e8e22d3121103";
832 #elif defined(OS_WIN)
833   const char kNonPrimesMD5[] = "86e371fdae30c2471f476631f3f93413";
834   const char kNonPrimesAfterSaveMD5[] = "86e371fdae30c2471f476631f3f93413";
835 #else
836   const char kNonPrimesMD5[] = "67ab13115d0cc34e99a1003c28047b40";
837   const char kNonPrimesAfterSaveMD5[] = "67ab13115d0cc34e99a1003c28047b40";
838 #endif
839   {
840     ScopedFPDFBitmap page_bitmap = RenderPage(page);
841     CompareBitmap(page_bitmap.get(), 200, 200, kNonPrimesMD5);
842   }
843 
844   // Save the file.
845   EXPECT_TRUE(FPDFPage_GenerateContent(page));
846   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
847   UnloadPage(page);
848 
849   // Re-open the file and check the prime marks are not there anymore.
850   ASSERT_TRUE(OpenSavedDocument());
851   FPDF_PAGE saved_page = LoadSavedPage(0);
852   EXPECT_EQ(11, FPDFPage_CountObjects(saved_page));
853 
854   {
855     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
856     CompareBitmap(page_bitmap.get(), 200, 200, kNonPrimesAfterSaveMD5);
857   }
858 
859   CloseSavedPage(saved_page);
860   CloseSavedDocument();
861 }
862 
TEST_F(FPDFEditEmbedderTest,RemoveMarks)863 TEST_F(FPDFEditEmbedderTest, RemoveMarks) {
864   // Load document with some text.
865   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
866   FPDF_PAGE page = LoadPage(0);
867   ASSERT_TRUE(page);
868 
869   constexpr int kExpectedObjectCount = 19;
870   CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
871 
872   // Remove all "Prime" content marks.
873   for (int i = 0; i < kExpectedObjectCount; ++i) {
874     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
875 
876     int mark_count = FPDFPageObj_CountMarks(page_object);
877     for (int j = mark_count - 1; j >= 0; --j) {
878       FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
879 
880       char buffer[256];
881       unsigned long name_len = 999u;
882       ASSERT_TRUE(
883           FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
884       EXPECT_GT(name_len, 0u);
885       EXPECT_NE(999u, name_len);
886       std::wstring name =
887           GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
888       if (name == L"Prime") {
889         // Remove mark.
890         EXPECT_TRUE(FPDFPageObj_RemoveMark(page_object, mark));
891 
892         // Verify there is now one fewer mark in the page object.
893         EXPECT_EQ(mark_count - 1, FPDFPageObj_CountMarks(page_object));
894       }
895     }
896   }
897 
898   // Verify there are 0 "Prime" content marks now.
899   CheckMarkCounts(page, 1, kExpectedObjectCount, 0, 4, 9, 1);
900 
901   // Save the file.
902   EXPECT_TRUE(FPDFPage_GenerateContent(page));
903   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
904   UnloadPage(page);
905 
906   // Re-open the file and check the prime marks are not there anymore.
907   ASSERT_TRUE(OpenSavedDocument());
908   FPDF_PAGE saved_page = LoadSavedPage(0);
909 
910   CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 0, 4, 9, 1);
911 
912   CloseSavedPage(saved_page);
913   CloseSavedDocument();
914 }
915 
TEST_F(FPDFEditEmbedderTest,RemoveMarkParam)916 TEST_F(FPDFEditEmbedderTest, RemoveMarkParam) {
917   // Load document with some text.
918   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
919   FPDF_PAGE page = LoadPage(0);
920   ASSERT_TRUE(page);
921 
922   constexpr int kExpectedObjectCount = 19;
923   CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
924 
925   // Remove all "Square" content marks parameters.
926   for (int i = 0; i < kExpectedObjectCount; ++i) {
927     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
928 
929     int mark_count = FPDFPageObj_CountMarks(page_object);
930     for (int j = 0; j < mark_count; ++j) {
931       FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
932 
933       char buffer[256];
934       unsigned long name_len = 999u;
935       ASSERT_TRUE(
936           FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
937       EXPECT_GT(name_len, 0u);
938       EXPECT_NE(999u, name_len);
939       std::wstring name =
940           GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
941       if (name == L"Square") {
942         // Show the mark has a "Factor" parameter.
943         int out_value;
944         EXPECT_TRUE(
945             FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
946 
947         // Remove parameter.
948         EXPECT_TRUE(FPDFPageObjMark_RemoveParam(page_object, mark, "Factor"));
949 
950         // Verify the "Factor" parameter is gone.
951         EXPECT_FALSE(
952             FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
953       }
954     }
955   }
956 
957   // Save the file.
958   EXPECT_TRUE(FPDFPage_GenerateContent(page));
959   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
960   UnloadPage(page);
961 
962   // Re-open the file and check the "Factor" parameters are still gone.
963   ASSERT_TRUE(OpenSavedDocument());
964   FPDF_PAGE saved_page = LoadSavedPage(0);
965 
966   size_t square_count = 0;
967   for (int i = 0; i < kExpectedObjectCount; ++i) {
968     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, i);
969 
970     int mark_count = FPDFPageObj_CountMarks(page_object);
971     for (int j = 0; j < mark_count; ++j) {
972       FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
973 
974       char buffer[256];
975       unsigned long name_len = 999u;
976       ASSERT_TRUE(
977           FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
978       EXPECT_GT(name_len, 0u);
979       EXPECT_NE(999u, name_len);
980       std::wstring name =
981           GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
982       if (name == L"Square") {
983         // Verify the "Factor" parameter is still gone.
984         int out_value;
985         EXPECT_FALSE(
986             FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
987 
988         ++square_count;
989       }
990     }
991   }
992 
993   // Verify the parameters are gone, but the marks are not.
994   EXPECT_EQ(4u, square_count);
995 
996   CloseSavedPage(saved_page);
997   CloseSavedDocument();
998 }
999 
TEST_F(FPDFEditEmbedderTest,MaintainMarkedObjects)1000 TEST_F(FPDFEditEmbedderTest, MaintainMarkedObjects) {
1001   // Load document with some text.
1002   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
1003   FPDF_PAGE page = LoadPage(0);
1004   ASSERT_TRUE(page);
1005 
1006   // Iterate over all objects, counting the number of times each content mark
1007   // name appears.
1008   CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
1009 
1010   // Remove first page object.
1011   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
1012   EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
1013   FPDFPageObj_Destroy(page_object);
1014 
1015   CheckMarkCounts(page, 2, 18, 8, 3, 9, 1);
1016 
1017   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1018   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1019 
1020   UnloadPage(page);
1021 
1022   ASSERT_TRUE(OpenSavedDocument());
1023   FPDF_PAGE saved_page = LoadSavedPage(0);
1024 
1025   CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1);
1026 
1027   CloseSavedPage(saved_page);
1028   CloseSavedDocument();
1029 }
1030 
TEST_F(FPDFEditEmbedderTest,MaintainIndirectMarkedObjects)1031 TEST_F(FPDFEditEmbedderTest, MaintainIndirectMarkedObjects) {
1032   // Load document with some text.
1033   EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
1034   FPDF_PAGE page = LoadPage(0);
1035   ASSERT_TRUE(page);
1036 
1037   // Iterate over all objects, counting the number of times each content mark
1038   // name appears.
1039   CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
1040 
1041   // Remove first page object.
1042   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
1043   EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
1044   FPDFPageObj_Destroy(page_object);
1045 
1046   CheckMarkCounts(page, 2, 18, 8, 3, 9, 1);
1047 
1048   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1049   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1050 
1051   UnloadPage(page);
1052 
1053   ASSERT_TRUE(OpenSavedDocument());
1054   FPDF_PAGE saved_page = LoadSavedPage(0);
1055 
1056   CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1);
1057 
1058   CloseSavedPage(saved_page);
1059   CloseSavedDocument();
1060 }
1061 
TEST_F(FPDFEditEmbedderTest,RemoveExistingPageObject)1062 TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObject) {
1063   // Load document with some text.
1064   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
1065   FPDF_PAGE page = LoadPage(0);
1066   ASSERT_TRUE(page);
1067 
1068   // Get the "Hello, world!" text object and remove it.
1069   ASSERT_EQ(2, FPDFPage_CountObjects(page));
1070   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
1071   ASSERT_TRUE(page_object);
1072   EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
1073 
1074   // Verify the "Hello, world!" text is gone.
1075   ASSERT_EQ(1, FPDFPage_CountObjects(page));
1076 
1077   // Save the file
1078   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1079   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1080   UnloadPage(page);
1081   FPDFPageObj_Destroy(page_object);
1082 
1083   // Re-open the file and check the page object count is still 1.
1084   ASSERT_TRUE(OpenSavedDocument());
1085   FPDF_PAGE saved_page = LoadSavedPage(0);
1086   EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
1087   CloseSavedPage(saved_page);
1088   CloseSavedDocument();
1089 }
1090 
1091 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1092 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1093 #define MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely \
1094   DISABLED_RemoveExistingPageObjectSplitStreamsNotLonely
1095 #else
1096 #define MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely \
1097   RemoveExistingPageObjectSplitStreamsNotLonely
1098 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely)1099 TEST_F(FPDFEditEmbedderTest,
1100        MAYBE_RemoveExistingPageObjectSplitStreamsNotLonely) {
1101   // Load document with some text.
1102   EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
1103   FPDF_PAGE page = LoadPage(0);
1104   ASSERT_TRUE(page);
1105 
1106   // Get the "Hello, world!" text object and remove it. There is another object
1107   // in the same stream that says "Goodbye, world!"
1108   ASSERT_EQ(3, FPDFPage_CountObjects(page));
1109   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
1110   ASSERT_TRUE(page_object);
1111   EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
1112 
1113   // Verify the "Hello, world!" text is gone.
1114   ASSERT_EQ(2, FPDFPage_CountObjects(page));
1115 #if defined(OS_MACOSX)
1116   const char kHelloRemovedMD5[] = "e07a62d412728fc4d6e3ff42f2dd0e11";
1117 #elif defined(OS_WIN)
1118   const char kHelloRemovedMD5[] = "a97d4c72c969ba373c2dce675d277e65";
1119 #else
1120   const char kHelloRemovedMD5[] = "95b92950647a2190e1230911e7a1a0e9";
1121 #endif
1122   {
1123     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1124     CompareBitmap(page_bitmap.get(), 200, 200, kHelloRemovedMD5);
1125   }
1126 
1127   // Save the file
1128   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1129   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1130   UnloadPage(page);
1131   FPDFPageObj_Destroy(page_object);
1132 
1133   // Re-open the file and check the page object count is still 2.
1134   ASSERT_TRUE(OpenSavedDocument());
1135   FPDF_PAGE saved_page = LoadSavedPage(0);
1136 
1137   EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
1138   {
1139     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1140     CompareBitmap(page_bitmap.get(), 200, 200, kHelloRemovedMD5);
1141   }
1142 
1143   CloseSavedPage(saved_page);
1144   CloseSavedDocument();
1145 }
1146 
1147 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1148 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1149 #define MAYBE_RemoveExistingPageObjectSplitStreamsLonely \
1150   DISABLED_RemoveExistingPageObjectSplitStreamsLonely
1151 #else
1152 #define MAYBE_RemoveExistingPageObjectSplitStreamsLonely \
1153   RemoveExistingPageObjectSplitStreamsLonely
1154 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_RemoveExistingPageObjectSplitStreamsLonely)1155 TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveExistingPageObjectSplitStreamsLonely) {
1156   // Load document with some text.
1157   EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
1158   FPDF_PAGE page = LoadPage(0);
1159   ASSERT_TRUE(page);
1160 
1161   // Get the "Greetings, world!" text object and remove it. This is the only
1162   // object in the stream.
1163   ASSERT_EQ(3, FPDFPage_CountObjects(page));
1164   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 2);
1165   ASSERT_TRUE(page_object);
1166   EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
1167 
1168   // Verify the "Greetings, world!" text is gone.
1169   ASSERT_EQ(2, FPDFPage_CountObjects(page));
1170 #if defined(OS_MACOSX)
1171   const char kGreetingsRemovedMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
1172 #elif defined(OS_WIN)
1173   const char kGreetingsRemovedMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
1174 #else
1175   const char kGreetingsRemovedMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
1176 #endif
1177   {
1178     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1179     CompareBitmap(page_bitmap.get(), 200, 200, kGreetingsRemovedMD5);
1180   }
1181 
1182   // Save the file
1183   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1184   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1185   UnloadPage(page);
1186   FPDFPageObj_Destroy(page_object);
1187 
1188   // Re-open the file and check the page object count is still 2.
1189   ASSERT_TRUE(OpenSavedDocument());
1190   FPDF_PAGE saved_page = LoadSavedPage(0);
1191 
1192   EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
1193   {
1194     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1195     CompareBitmap(page_bitmap.get(), 200, 200, kGreetingsRemovedMD5);
1196   }
1197 
1198   CloseSavedPage(saved_page);
1199   CloseSavedDocument();
1200 }
1201 
TEST_F(FPDFEditEmbedderTest,GetContentStream)1202 TEST_F(FPDFEditEmbedderTest, GetContentStream) {
1203   // Load document with some text split across streams.
1204   EXPECT_TRUE(OpenDocument("split_streams.pdf"));
1205   FPDF_PAGE page = LoadPage(0);
1206   ASSERT_TRUE(page);
1207 
1208   // Content stream 0: page objects 0-14.
1209   // Content stream 1: page objects 15-17.
1210   // Content stream 2: page object 18.
1211   ASSERT_EQ(19, FPDFPage_CountObjects(page));
1212   for (int i = 0; i < 19; i++) {
1213     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
1214     ASSERT_TRUE(page_object);
1215     CPDF_PageObject* cpdf_page_object =
1216         CPDFPageObjectFromFPDFPageObject(page_object);
1217     if (i < 15)
1218       EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
1219     else if (i < 18)
1220       EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
1221     else
1222       EXPECT_EQ(2, cpdf_page_object->GetContentStream()) << i;
1223   }
1224 
1225   UnloadPage(page);
1226 }
1227 
1228 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1229 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1230 #define MAYBE_RemoveAllFromStream DISABLED_RemoveAllFromStream
1231 #else
1232 #define MAYBE_RemoveAllFromStream RemoveAllFromStream
1233 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_RemoveAllFromStream)1234 TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveAllFromStream) {
1235   // Load document with some text split across streams.
1236   EXPECT_TRUE(OpenDocument("split_streams.pdf"));
1237   FPDF_PAGE page = LoadPage(0);
1238   ASSERT_TRUE(page);
1239 
1240   // Content stream 0: page objects 0-14.
1241   // Content stream 1: page objects 15-17.
1242   // Content stream 2: page object 18.
1243   ASSERT_EQ(19, FPDFPage_CountObjects(page));
1244 
1245   // Loop backwards because objects will being removed, which shifts the indexes
1246   // after the removed position.
1247   for (int i = 18; i >= 0; i--) {
1248     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
1249     ASSERT_TRUE(page_object);
1250     CPDF_PageObject* cpdf_page_object =
1251         CPDFPageObjectFromFPDFPageObject(page_object);
1252 
1253     // Empty content stream 1.
1254     if (cpdf_page_object->GetContentStream() == 1) {
1255       EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
1256       FPDFPageObj_Destroy(page_object);
1257     }
1258   }
1259 
1260   // Content stream 0: page objects 0-14.
1261   // Content stream 2: page object 15.
1262   ASSERT_EQ(16, FPDFPage_CountObjects(page));
1263   for (int i = 0; i < 16; i++) {
1264     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
1265     ASSERT_TRUE(page_object);
1266     CPDF_PageObject* cpdf_page_object =
1267         CPDFPageObjectFromFPDFPageObject(page_object);
1268     if (i < 15)
1269       EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
1270     else
1271       EXPECT_EQ(2, cpdf_page_object->GetContentStream()) << i;
1272   }
1273 
1274   // Generate contents should remove the empty stream and update the page
1275   // objects' contents stream indexes.
1276   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1277 
1278   // Content stream 0: page objects 0-14.
1279   // Content stream 1: page object 15.
1280   ASSERT_EQ(16, FPDFPage_CountObjects(page));
1281   for (int i = 0; i < 16; i++) {
1282     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
1283     ASSERT_TRUE(page_object);
1284     CPDF_PageObject* cpdf_page_object =
1285         CPDFPageObjectFromFPDFPageObject(page_object);
1286     if (i < 15)
1287       EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
1288     else
1289       EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
1290   }
1291 
1292 #if defined(OS_MACOSX)
1293   const char kStream1RemovedMD5[] = "d2e21fbd5a6de563f619feeeb6163331";
1294 #elif defined(OS_WIN)
1295   const char kStream1RemovedMD5[] = "b4140f203523e38793283a5943d8075b";
1296 #else
1297   const char kStream1RemovedMD5[] = "e86a3efc160ede6cfcb1f59bcacf1105";
1298 #endif
1299   {
1300     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1301     CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5);
1302   }
1303 
1304   // Save the file
1305   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1306   UnloadPage(page);
1307 
1308   // Re-open the file and check the page object count is still 16, and that
1309   // content stream 1 was removed.
1310   ASSERT_TRUE(OpenSavedDocument());
1311   FPDF_PAGE saved_page = LoadSavedPage(0);
1312 
1313   // Content stream 0: page objects 0-14.
1314   // Content stream 1: page object 15.
1315   EXPECT_EQ(16, FPDFPage_CountObjects(saved_page));
1316   for (int i = 0; i < 16; i++) {
1317     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, i);
1318     ASSERT_TRUE(page_object);
1319     CPDF_PageObject* cpdf_page_object =
1320         CPDFPageObjectFromFPDFPageObject(page_object);
1321     if (i < 15)
1322       EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
1323     else
1324       EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
1325   }
1326 
1327   {
1328     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1329     CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5);
1330   }
1331 
1332   CloseSavedPage(saved_page);
1333   CloseSavedDocument();
1334 }
1335 
TEST_F(FPDFEditEmbedderTest,RemoveAllFromSingleStream)1336 TEST_F(FPDFEditEmbedderTest, RemoveAllFromSingleStream) {
1337   // Load document with a single stream.
1338   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
1339   FPDF_PAGE page = LoadPage(0);
1340   ASSERT_TRUE(page);
1341 
1342   // Content stream 0: page objects 0-1.
1343   ASSERT_EQ(2, FPDFPage_CountObjects(page));
1344 
1345   // Loop backwards because objects will being removed, which shifts the indexes
1346   // after the removed position.
1347   for (int i = 1; i >= 0; i--) {
1348     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
1349     ASSERT_TRUE(page_object);
1350     CPDF_PageObject* cpdf_page_object =
1351         CPDFPageObjectFromFPDFPageObject(page_object);
1352     ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1353     ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object));
1354     FPDFPageObj_Destroy(page_object);
1355   }
1356 
1357   // No more objects in the stream
1358   ASSERT_EQ(0, FPDFPage_CountObjects(page));
1359 
1360   // Generate contents should remove the empty stream and update the page
1361   // objects' contents stream indexes.
1362   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1363 
1364   ASSERT_EQ(0, FPDFPage_CountObjects(page));
1365 
1366   const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c";
1367   {
1368     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1369     CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
1370   }
1371 
1372   // Save the file
1373   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1374   UnloadPage(page);
1375 
1376   // Re-open the file and check the page object count is still 0.
1377   ASSERT_TRUE(OpenSavedDocument());
1378   FPDF_PAGE saved_page = LoadSavedPage(0);
1379 
1380   EXPECT_EQ(0, FPDFPage_CountObjects(saved_page));
1381   {
1382     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1383     CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
1384   }
1385 
1386   CloseSavedPage(saved_page);
1387   CloseSavedDocument();
1388 }
1389 
1390 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1391 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1392 #define MAYBE_RemoveFirstFromSingleStream DISABLED_RemoveFirstFromSingleStream
1393 #else
1394 #define MAYBE_RemoveFirstFromSingleStream RemoveFirstFromSingleStream
1395 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_RemoveFirstFromSingleStream)1396 TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveFirstFromSingleStream) {
1397   // Load document with a single stream.
1398   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
1399   FPDF_PAGE page = LoadPage(0);
1400   ASSERT_TRUE(page);
1401 
1402   // Content stream 0: page objects 0-1.
1403   ASSERT_EQ(2, FPDFPage_CountObjects(page));
1404 
1405   // Remove first object.
1406   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
1407   ASSERT_TRUE(page_object);
1408   CPDF_PageObject* cpdf_page_object =
1409       CPDFPageObjectFromFPDFPageObject(page_object);
1410   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1411   ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object));
1412   FPDFPageObj_Destroy(page_object);
1413 
1414   // One object left in the stream.
1415   ASSERT_EQ(1, FPDFPage_CountObjects(page));
1416   page_object = FPDFPage_GetObject(page, 0);
1417   ASSERT_TRUE(page_object);
1418   cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
1419   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1420 
1421   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1422 
1423   // Still one object left in the stream.
1424   ASSERT_EQ(1, FPDFPage_CountObjects(page));
1425   page_object = FPDFPage_GetObject(page, 0);
1426   ASSERT_TRUE(page_object);
1427   cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
1428   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1429 
1430 #if defined(OS_MACOSX)
1431   const char kFirstRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa";
1432 #elif defined(OS_WIN)
1433   const char kFirstRemovedMD5[] = "aae6c5334721f90ec30d3d59f4ef7deb";
1434 #else
1435   const char kFirstRemovedMD5[] = "b76df015fe88009c3c342395df96abf1";
1436 #endif
1437   {
1438     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1439     CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5);
1440   }
1441 
1442   // Save the file
1443   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1444   UnloadPage(page);
1445 
1446   // Re-open the file and check the page object count is still 0.
1447   ASSERT_TRUE(OpenSavedDocument());
1448   FPDF_PAGE saved_page = LoadSavedPage(0);
1449 
1450   ASSERT_EQ(1, FPDFPage_CountObjects(saved_page));
1451   page_object = FPDFPage_GetObject(saved_page, 0);
1452   ASSERT_TRUE(page_object);
1453   cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
1454   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1455   {
1456     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1457     CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5);
1458   }
1459 
1460   CloseSavedPage(saved_page);
1461   CloseSavedDocument();
1462 }
1463 
1464 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1465 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1466 #define MAYBE_RemoveLastFromSingleStream DISABLED_RemoveLastFromSingleStream
1467 #else
1468 #define MAYBE_RemoveLastFromSingleStream RemoveLastFromSingleStream
1469 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_RemoveLastFromSingleStream)1470 TEST_F(FPDFEditEmbedderTest, MAYBE_RemoveLastFromSingleStream) {
1471   // Load document with a single stream.
1472   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
1473   FPDF_PAGE page = LoadPage(0);
1474   ASSERT_TRUE(page);
1475 
1476   // Content stream 0: page objects 0-1.
1477   ASSERT_EQ(2, FPDFPage_CountObjects(page));
1478 
1479   // Remove last object
1480   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 1);
1481   ASSERT_TRUE(page_object);
1482   CPDF_PageObject* cpdf_page_object =
1483       CPDFPageObjectFromFPDFPageObject(page_object);
1484   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1485   ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object));
1486   FPDFPageObj_Destroy(page_object);
1487 
1488   // One object left in the stream.
1489   ASSERT_EQ(1, FPDFPage_CountObjects(page));
1490   page_object = FPDFPage_GetObject(page, 0);
1491   ASSERT_TRUE(page_object);
1492   cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
1493   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1494 
1495   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1496 
1497   // Still one object left in the stream.
1498   ASSERT_EQ(1, FPDFPage_CountObjects(page));
1499   page_object = FPDFPage_GetObject(page, 0);
1500   ASSERT_TRUE(page_object);
1501   cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
1502   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1503 
1504 #if defined(OS_MACOSX)
1505   const char kLastRemovedMD5[] = "f8fbd14a048b9e2ea8e5f059f22a910e";
1506 #elif defined(OS_WIN)
1507   const char kLastRemovedMD5[] = "93db13099042bafefb3c22a165bad684";
1508 #else
1509   const char kLastRemovedMD5[] = "93dcc09055f87a2792c8e3065af99a1b";
1510 #endif
1511   {
1512     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1513     CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5);
1514   }
1515 
1516   // Save the file
1517   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1518   UnloadPage(page);
1519 
1520   // Re-open the file and check the page object count is still 0.
1521   ASSERT_TRUE(OpenSavedDocument());
1522   FPDF_PAGE saved_page = LoadSavedPage(0);
1523 
1524   ASSERT_EQ(1, FPDFPage_CountObjects(saved_page));
1525   page_object = FPDFPage_GetObject(saved_page, 0);
1526   ASSERT_TRUE(page_object);
1527   cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
1528   ASSERT_EQ(0, cpdf_page_object->GetContentStream());
1529   {
1530     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1531     CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5);
1532   }
1533 
1534   CloseSavedPage(saved_page);
1535   CloseSavedDocument();
1536 }
1537 
TEST_F(FPDFEditEmbedderTest,RemoveAllFromMultipleStreams)1538 TEST_F(FPDFEditEmbedderTest, RemoveAllFromMultipleStreams) {
1539   // Load document with some text.
1540   EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
1541   FPDF_PAGE page = LoadPage(0);
1542   ASSERT_TRUE(page);
1543 
1544   // Content stream 0: page objects 0-1.
1545   // Content stream 1: page object 2.
1546   ASSERT_EQ(3, FPDFPage_CountObjects(page));
1547 
1548   // Loop backwards because objects will being removed, which shifts the indexes
1549   // after the removed position.
1550   for (int i = 2; i >= 0; i--) {
1551     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
1552     ASSERT_TRUE(page_object);
1553     ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object));
1554     FPDFPageObj_Destroy(page_object);
1555   }
1556 
1557   // No more objects in the page.
1558   ASSERT_EQ(0, FPDFPage_CountObjects(page));
1559 
1560   // Generate contents should remove the empty streams and update the page
1561   // objects' contents stream indexes.
1562   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1563 
1564   ASSERT_EQ(0, FPDFPage_CountObjects(page));
1565 
1566   const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c";
1567   {
1568     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1569     CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
1570   }
1571 
1572   // Save the file
1573   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1574   UnloadPage(page);
1575 
1576   // Re-open the file and check the page object count is still 0.
1577   ASSERT_TRUE(OpenSavedDocument());
1578   FPDF_PAGE saved_page = LoadSavedPage(0);
1579 
1580   EXPECT_EQ(0, FPDFPage_CountObjects(saved_page));
1581   {
1582     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1583     CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5);
1584   }
1585 
1586   CloseSavedPage(saved_page);
1587   CloseSavedDocument();
1588 }
1589 
TEST_F(FPDFEditEmbedderTest,InsertPageObjectAndSave)1590 TEST_F(FPDFEditEmbedderTest, InsertPageObjectAndSave) {
1591   // Load document with some text.
1592   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
1593   FPDF_PAGE page = LoadPage(0);
1594   ASSERT_TRUE(page);
1595 
1596   // Add a red rectangle.
1597   ASSERT_EQ(2, FPDFPage_CountObjects(page));
1598   FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
1599   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
1600   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
1601   FPDFPage_InsertObject(page, red_rect);
1602 
1603   // Verify the red rectangle was added.
1604   ASSERT_EQ(3, FPDFPage_CountObjects(page));
1605 
1606   // Save the file
1607   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1608   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1609   UnloadPage(page);
1610 
1611   // Re-open the file and check the page object count is still 3.
1612   ASSERT_TRUE(OpenSavedDocument());
1613   FPDF_PAGE saved_page = LoadSavedPage(0);
1614   EXPECT_EQ(3, FPDFPage_CountObjects(saved_page));
1615   CloseSavedPage(saved_page);
1616   CloseSavedDocument();
1617 }
1618 
TEST_F(FPDFEditEmbedderTest,InsertPageObjectEditAndSave)1619 TEST_F(FPDFEditEmbedderTest, InsertPageObjectEditAndSave) {
1620   // Load document with some text.
1621   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
1622   FPDF_PAGE page = LoadPage(0);
1623   ASSERT_TRUE(page);
1624 
1625   // Add a red rectangle.
1626   ASSERT_EQ(2, FPDFPage_CountObjects(page));
1627   FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
1628   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 100, 100, 255));
1629   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
1630   FPDFPage_InsertObject(page, red_rect);
1631 
1632   // Verify the red rectangle was added.
1633   ASSERT_EQ(3, FPDFPage_CountObjects(page));
1634 
1635   // Generate content but change it again
1636   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1637   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
1638 
1639   // Save the file
1640   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1641   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1642   UnloadPage(page);
1643 
1644   // Re-open the file and check the page object count is still 3.
1645   ASSERT_TRUE(OpenSavedDocument());
1646   FPDF_PAGE saved_page = LoadSavedPage(0);
1647   EXPECT_EQ(3, FPDFPage_CountObjects(saved_page));
1648   CloseSavedPage(saved_page);
1649   CloseSavedDocument();
1650 }
1651 
1652 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1653 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1654 #define MAYBE_InsertAndRemoveLargeFile DISABLED_InsertAndRemoveLargeFile
1655 #else
1656 #define MAYBE_InsertAndRemoveLargeFile InsertAndRemoveLargeFile
1657 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_InsertAndRemoveLargeFile)1658 TEST_F(FPDFEditEmbedderTest, MAYBE_InsertAndRemoveLargeFile) {
1659   const int kOriginalObjectCount = 600;
1660 
1661   // Load document with many objects.
1662   EXPECT_TRUE(OpenDocument("many_rectangles.pdf"));
1663   FPDF_PAGE page = LoadPage(0);
1664   ASSERT_TRUE(page);
1665   const char kOriginalMD5[] = "b0170c575b65ecb93ebafada0ff0f038";
1666   {
1667     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1668     CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
1669   }
1670 
1671   // Add a black rectangle.
1672   ASSERT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(page));
1673   FPDF_PAGEOBJECT black_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
1674   EXPECT_TRUE(FPDFPageObj_SetFillColor(black_rect, 0, 0, 0, 255));
1675   EXPECT_TRUE(FPDFPath_SetDrawMode(black_rect, FPDF_FILLMODE_ALTERNATE, 0));
1676   FPDFPage_InsertObject(page, black_rect);
1677 
1678   // Verify the black rectangle was added.
1679   ASSERT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(page));
1680   const char kPlusRectangleMD5[] = "6b9396ab570754b32b04ca629e902f77";
1681   {
1682     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1683     CompareBitmap(page_bitmap.get(), 200, 300, kPlusRectangleMD5);
1684   }
1685 
1686   // Save the file.
1687   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1688   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1689   UnloadPage(page);
1690 
1691   // Re-open the file and check the rectangle added is still there.
1692   ASSERT_TRUE(OpenSavedDocument());
1693   FPDF_PAGE saved_page = LoadSavedPage(0);
1694   EXPECT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(saved_page));
1695   {
1696     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1697     CompareBitmap(page_bitmap.get(), 200, 300, kPlusRectangleMD5);
1698   }
1699 
1700   // Remove the added rectangle.
1701   FPDF_PAGEOBJECT added_object =
1702       FPDFPage_GetObject(saved_page, kOriginalObjectCount);
1703   EXPECT_TRUE(FPDFPage_RemoveObject(saved_page, added_object));
1704   FPDFPageObj_Destroy(added_object);
1705   {
1706     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1707     CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
1708   }
1709   EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page));
1710 
1711   // Save the file again.
1712   EXPECT_TRUE(FPDFPage_GenerateContent(saved_page));
1713   EXPECT_TRUE(FPDF_SaveAsCopy(saved_document_, this, 0));
1714 
1715   CloseSavedPage(saved_page);
1716   CloseSavedDocument();
1717 
1718   // Re-open the file (again) and check the black rectangle was removed and the
1719   // rest is intact.
1720   ASSERT_TRUE(OpenSavedDocument());
1721   saved_page = LoadSavedPage(0);
1722   EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page));
1723   {
1724     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1725     CompareBitmap(page_bitmap.get(), 200, 300, kOriginalMD5);
1726   }
1727 
1728   CloseSavedPage(saved_page);
1729   CloseSavedDocument();
1730 }
1731 
TEST_F(FPDFEditEmbedderTest,AddAndRemovePaths)1732 TEST_F(FPDFEditEmbedderTest, AddAndRemovePaths) {
1733   // Start with a blank page.
1734   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
1735   ASSERT_TRUE(page);
1736 
1737   // Render the blank page and verify it's a blank bitmap.
1738   const char kBlankMD5[] = "1940568c9ba33bac5d0b1ee9558c76b3";
1739   {
1740     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1741     CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5);
1742   }
1743   ASSERT_EQ(0, FPDFPage_CountObjects(page));
1744 
1745   // Add a red rectangle.
1746   FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
1747   ASSERT_TRUE(red_rect);
1748   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
1749   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
1750   FPDFPage_InsertObject(page, red_rect);
1751   const char kRedRectangleMD5[] = "66d02eaa6181e2c069ce2ea99beda497";
1752   {
1753     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1754     CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleMD5);
1755   }
1756   EXPECT_EQ(1, FPDFPage_CountObjects(page));
1757 
1758   // Remove rectangle and verify it does not render anymore and the bitmap is
1759   // back to a blank one.
1760   EXPECT_TRUE(FPDFPage_RemoveObject(page, red_rect));
1761   {
1762     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1763     CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5);
1764   }
1765   EXPECT_EQ(0, FPDFPage_CountObjects(page));
1766 
1767   // Trying to remove an object not in the page should return false.
1768   EXPECT_FALSE(FPDFPage_RemoveObject(page, red_rect));
1769 
1770   FPDF_ClosePage(page);
1771   FPDFPageObj_Destroy(red_rect);
1772 }
1773 
TEST_F(FPDFEditEmbedderTest,PathsPoints)1774 TEST_F(FPDFEditEmbedderTest, PathsPoints) {
1775   CreateNewDocument();
1776   FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document_);
1777   // This should fail gracefully, even if img is not a path.
1778   ASSERT_EQ(-1, FPDFPath_CountSegments(img));
1779 
1780   // This should fail gracefully, even if path is NULL.
1781   ASSERT_EQ(-1, FPDFPath_CountSegments(nullptr));
1782 
1783   // FPDFPath_GetPathSegment() with a non-path.
1784   ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(img, 0));
1785   // FPDFPath_GetPathSegment() with a NULL path.
1786   ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(nullptr, 0));
1787   float x;
1788   float y;
1789   // FPDFPathSegment_GetPoint() with a NULL segment.
1790   EXPECT_FALSE(FPDFPathSegment_GetPoint(nullptr, &x, &y));
1791 
1792   // FPDFPathSegment_GetType() with a NULL segment.
1793   ASSERT_EQ(FPDF_SEGMENT_UNKNOWN, FPDFPathSegment_GetType(nullptr));
1794 
1795   // FPDFPathSegment_GetClose() with a NULL segment.
1796   EXPECT_FALSE(FPDFPathSegment_GetClose(nullptr));
1797 
1798   FPDFPageObj_Destroy(img);
1799 }
1800 
1801 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1802 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1803 #define MAYBE_PathOnTopOfText DISABLED_PathOnTopOfText
1804 #else
1805 #define MAYBE_PathOnTopOfText PathOnTopOfText
1806 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_PathOnTopOfText)1807 TEST_F(FPDFEditEmbedderTest, MAYBE_PathOnTopOfText) {
1808   // Load document with some text
1809   EXPECT_TRUE(OpenDocument("hello_world.pdf"));
1810   FPDF_PAGE page = LoadPage(0);
1811   ASSERT_TRUE(page);
1812 
1813   // Add an opaque rectangle on top of some of the text.
1814   FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
1815   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
1816   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
1817   FPDFPage_InsertObject(page, red_rect);
1818 
1819   // Add a transparent triangle on top of other part of the text.
1820   FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
1821   EXPECT_TRUE(FPDFPageObj_SetFillColor(black_path, 0, 0, 0, 100));
1822   EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
1823   EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
1824   EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
1825   EXPECT_TRUE(FPDFPath_Close(black_path));
1826   FPDFPage_InsertObject(page, black_path);
1827 
1828   // Render and check the result. Text is slightly different on Mac.
1829   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
1830 #if defined(OS_MACOSX)
1831   const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
1832 #elif defined(OS_WIN)
1833   const char md5[] = "74dd9c393b8b2578d2b7feb032b7daad";
1834 #else
1835   const char md5[] = "aa71b09b93b55f467f1290e5111babee";
1836 #endif
1837   CompareBitmap(bitmap.get(), 200, 200, md5);
1838   UnloadPage(page);
1839 }
1840 
1841 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1842 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1843 #define MAYBE_EditOverExistingContent DISABLED_EditOverExistingContent
1844 #else
1845 #define MAYBE_EditOverExistingContent EditOverExistingContent
1846 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_EditOverExistingContent)1847 TEST_F(FPDFEditEmbedderTest, MAYBE_EditOverExistingContent) {
1848   // Load document with existing content
1849   EXPECT_TRUE(OpenDocument("bug_717.pdf"));
1850   FPDF_PAGE page = LoadPage(0);
1851   ASSERT_TRUE(page);
1852 
1853   // Add a transparent rectangle on top of the existing content
1854   FPDF_PAGEOBJECT red_rect2 = FPDFPageObj_CreateNewRect(90, 700, 25, 50);
1855   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect2, 255, 0, 0, 100));
1856   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect2, FPDF_FILLMODE_ALTERNATE, 0));
1857   FPDFPage_InsertObject(page, red_rect2);
1858 
1859   // Add an opaque rectangle on top of the existing content
1860   FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(115, 700, 25, 50);
1861   EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
1862   EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
1863   FPDFPage_InsertObject(page, red_rect);
1864 
1865   ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
1866   CompareBitmap(bitmap.get(), 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
1867   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1868 
1869   // Now save the result, closing the page and document
1870   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1871   UnloadPage(page);
1872 
1873   ASSERT_TRUE(OpenSavedDocument());
1874   FPDF_PAGE saved_page = LoadSavedPage(0);
1875   VerifySavedRendering(saved_page, 612, 792,
1876                        "ad04e5bd0f471a9a564fb034bd0fb073");
1877 
1878   ClearString();
1879   // Add another opaque rectangle on top of the existing content
1880   FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(150, 700, 25, 50);
1881   EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect, 0, 255, 0, 255));
1882   EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_ALTERNATE, 0));
1883   FPDFPage_InsertObject(saved_page, green_rect);
1884 
1885   // Add another transparent rectangle on top of existing content
1886   FPDF_PAGEOBJECT green_rect2 = FPDFPageObj_CreateNewRect(175, 700, 25, 50);
1887   EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect2, 0, 255, 0, 100));
1888   EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect2, FPDF_FILLMODE_ALTERNATE, 0));
1889   FPDFPage_InsertObject(saved_page, green_rect2);
1890   const char kLastMD5[] = "4b5b00f824620f8c9b8801ebb98e1cdd";
1891   {
1892     ScopedFPDFBitmap new_bitmap = RenderSavedPage(saved_page);
1893     CompareBitmap(new_bitmap.get(), 612, 792, kLastMD5);
1894   }
1895   EXPECT_TRUE(FPDFPage_GenerateContent(saved_page));
1896 
1897   // Now save the result, closing the page and document
1898   EXPECT_TRUE(FPDF_SaveAsCopy(saved_document_, this, 0));
1899 
1900   CloseSavedPage(saved_page);
1901   CloseSavedDocument();
1902 
1903   // Render the saved result
1904   VerifySavedDocument(612, 792, kLastMD5);
1905 }
1906 
1907 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1908 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1909 #define MAYBE_AddStrokedPaths DISABLED_AddStrokedPaths
1910 #else
1911 #define MAYBE_AddStrokedPaths AddStrokedPaths
1912 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_AddStrokedPaths)1913 TEST_F(FPDFEditEmbedderTest, MAYBE_AddStrokedPaths) {
1914   // Start with a blank page
1915   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
1916 
1917   // Add a large stroked rectangle (fill color should not affect it).
1918   FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
1919   EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 255, 0, 0, 255));
1920   EXPECT_TRUE(FPDFPageObj_SetStrokeColor(rect, 0, 255, 0, 255));
1921   EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(rect, 15.0f));
1922 
1923   float width = 0;
1924   EXPECT_TRUE(FPDFPageObj_GetStrokeWidth(rect, &width));
1925   EXPECT_EQ(15.0f, width);
1926 
1927   EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
1928   FPDFPage_InsertObject(page, rect);
1929   {
1930     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1931     CompareBitmap(page_bitmap.get(), 612, 792,
1932                   "64bd31f862a89e0a9e505a5af6efd506");
1933   }
1934 
1935   // Add crossed-checkmark
1936   FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
1937   EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
1938   EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
1939   EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
1940   EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
1941   EXPECT_TRUE(FPDFPageObj_SetStrokeColor(check, 128, 128, 128, 180));
1942   EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(check, 8.35f));
1943   EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
1944   FPDFPage_InsertObject(page, check);
1945   {
1946     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1947     CompareBitmap(page_bitmap.get(), 612, 792,
1948                   "4b6f3b9d25c4e194821217d5016c3724");
1949   }
1950 
1951   // Add stroked and filled oval-ish path.
1952   FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
1953   EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
1954   EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
1955   EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
1956   EXPECT_TRUE(FPDFPath_Close(path));
1957   EXPECT_TRUE(FPDFPageObj_SetFillColor(path, 200, 128, 128, 100));
1958   EXPECT_TRUE(FPDFPageObj_SetStrokeColor(path, 128, 200, 128, 150));
1959   EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(path, 10.5f));
1960   EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
1961   FPDFPage_InsertObject(page, path);
1962   {
1963     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1964     CompareBitmap(page_bitmap.get(), 612, 792,
1965                   "ff3e6a22326754944cc6e56609acd73b");
1966   }
1967   FPDF_ClosePage(page);
1968 }
1969 
1970 // Tests adding text from standard font using FPDFPageObj_NewTextObj.
1971 // TODO(crbug.com/pdfium/11): Fix this test and enable.
1972 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1973 #define MAYBE_AddStandardFontText DISABLED_AddStandardFontText
1974 #else
1975 #define MAYBE_AddStandardFontText AddStandardFontText
1976 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_AddStandardFontText)1977 TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText) {
1978   // Start with a blank page
1979   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
1980 
1981   // Add some text to the page
1982   FPDF_PAGEOBJECT text_object1 =
1983       FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
1984   EXPECT_TRUE(text_object1);
1985   ScopedFPDFWideString text1 =
1986       GetFPDFWideString(L"I'm at the bottom of the page");
1987   EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get()));
1988   FPDFPageObj_Transform(text_object1, 1, 0, 0, 1, 20, 20);
1989   FPDFPage_InsertObject(page, text_object1);
1990   EXPECT_TRUE(FPDFPage_GenerateContent(page));
1991   {
1992     ScopedFPDFBitmap page_bitmap = RenderPage(page);
1993 #if defined(OS_MACOSX)
1994     const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
1995 #elif defined(OS_WIN)
1996     const char md5[] = "08d1ff3e5a42801bee6077fd366bef00";
1997 #else
1998     const char md5[] = "eacaa24573b8ce997b3882595f096f00";
1999 #endif
2000     CompareBitmap(page_bitmap.get(), 612, 792, md5);
2001 
2002     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2003     VerifySavedDocument(612, 792, md5);
2004   }
2005 
2006   // Try another font
2007   FPDF_PAGEOBJECT text_object2 =
2008       FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
2009   EXPECT_TRUE(text_object2);
2010   ScopedFPDFWideString text2 =
2011       GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold.");
2012   EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
2013   FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600);
2014   FPDFPage_InsertObject(page, text_object2);
2015   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2016   {
2017     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2018 #if defined(OS_MACOSX)
2019     const char md5[] = "a5c4ace4c6f27644094813fe1441a21c";
2020 #elif defined(OS_WIN)
2021     const char md5[] = "3755dd35abd4c605755369401ee85b2d";
2022 #else
2023     const char md5[] = "76fcc7d08aa15445efd2e2ceb7c6cc3b";
2024 #endif
2025     CompareBitmap(page_bitmap.get(), 612, 792, md5);
2026 
2027     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2028     VerifySavedDocument(612, 792, md5);
2029   }
2030 
2031   // And some randomly transformed text
2032   FPDF_PAGEOBJECT text_object3 =
2033       FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
2034   EXPECT_TRUE(text_object3);
2035   ScopedFPDFWideString text3 = GetFPDFWideString(L"Can you read me? <:)>");
2036   EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get()));
2037   FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200);
2038   FPDFPage_InsertObject(page, text_object3);
2039   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2040   {
2041     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2042 #if defined(OS_MACOSX)
2043     const char md5[] = "40b3ef04f915ff4c4208948001763544";
2044 #elif defined(OS_WIN)
2045     const char md5[] = "5ded49fe157f89627903553771431e3d";
2046 #else
2047     const char md5[] = "344534539aa7c5cc78404cfff4bde7fb";
2048 #endif
2049     CompareBitmap(page_bitmap.get(), 612, 792, md5);
2050 
2051     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2052     VerifySavedDocument(612, 792, md5);
2053   }
2054 
2055   FS_MATRIX matrix;
2056   EXPECT_FALSE(FPDFTextObj_GetMatrix(nullptr, &matrix));
2057   EXPECT_TRUE(FPDFTextObj_GetMatrix(text_object3, &matrix));
2058   EXPECT_FLOAT_EQ(1.0f, matrix.a);
2059   EXPECT_FLOAT_EQ(1.5f, matrix.b);
2060   EXPECT_FLOAT_EQ(2.0f, matrix.c);
2061   EXPECT_FLOAT_EQ(0.5f, matrix.d);
2062   EXPECT_FLOAT_EQ(200.0f, matrix.e);
2063   EXPECT_FLOAT_EQ(200.0f, matrix.f);
2064 
2065   EXPECT_EQ(0, FPDFTextObj_GetFontSize(nullptr));
2066   EXPECT_EQ(20, FPDFTextObj_GetFontSize(text_object3));
2067 
2068   // TODO(npm): Why are there issues with text rotated by 90 degrees?
2069   // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
2070   FPDF_ClosePage(page);
2071 }
2072 
TEST_F(FPDFEditEmbedderTest,GetTextRenderMode)2073 TEST_F(FPDFEditEmbedderTest, GetTextRenderMode) {
2074   EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
2075   FPDF_PAGE page = LoadPage(0);
2076   ASSERT_TRUE(page);
2077   ASSERT_EQ(2, FPDFPage_CountObjects(page));
2078 
2079   EXPECT_EQ(FPDF_TEXTRENDERMODE_UNKNOWN,
2080             FPDFTextObj_GetTextRenderMode(nullptr));
2081 
2082   FPDF_PAGEOBJECT fill = FPDFPage_GetObject(page, 0);
2083   EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL, FPDFTextObj_GetTextRenderMode(fill));
2084 
2085   FPDF_PAGEOBJECT stroke = FPDFPage_GetObject(page, 1);
2086   EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE, FPDFTextObj_GetTextRenderMode(stroke));
2087 
2088   UnloadPage(page);
2089 }
2090 
TEST_F(FPDFEditEmbedderTest,SetTextRenderMode)2091 TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) {
2092   EXPECT_TRUE(OpenDocument("text_render_mode.pdf"));
2093   FPDF_PAGE page = LoadPage(0);
2094   ASSERT_TRUE(page);
2095   ASSERT_EQ(2, FPDFPage_CountObjects(page));
2096 
2097   // Check the bitmap
2098   {
2099 #if defined(OS_MACOSX)
2100     const char md5[] = "139846b4ffbd34b1fd67e3b82cf33b7e";
2101 #elif defined(OS_WIN)
2102     const char md5[] = "de6e86bad3e9fda753a8471a45cfbb58";
2103 #else
2104     const char md5[] = "5a012d2920ac075c39ffa9437ea42faa";
2105 #endif
2106     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2107     CompareBitmap(page_bitmap.get(), 612, 446, md5);
2108   }
2109 
2110   // Cannot set on a null object.
2111   EXPECT_FALSE(
2112       FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_UNKNOWN));
2113   EXPECT_FALSE(
2114       FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_INVISIBLE));
2115 
2116   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
2117   ASSERT_TRUE(page_object);
2118   EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL,
2119             FPDFTextObj_GetTextRenderMode(page_object));
2120 
2121   // Cannot set UNKNOWN as a render mode.
2122   EXPECT_FALSE(
2123       FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_UNKNOWN));
2124 
2125   EXPECT_TRUE(
2126       FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_STROKE));
2127   EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE,
2128             FPDFTextObj_GetTextRenderMode(page_object));
2129 
2130   // Check that bitmap displays changed content
2131   {
2132     const char md5[] = "412e52e621b46bd77baf2162e1fb1a1d";
2133     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2134     CompareBitmap(page_bitmap.get(), 612, 446, md5);
2135   }
2136 
2137   UnloadPage(page);
2138 }
2139 
TEST_F(FPDFEditEmbedderTest,TestGetTextFontName)2140 TEST_F(FPDFEditEmbedderTest, TestGetTextFontName) {
2141   EXPECT_TRUE(OpenDocument("text_font.pdf"));
2142   FPDF_PAGE page = LoadPage(0);
2143   ASSERT_TRUE(page);
2144   ASSERT_EQ(1, FPDFPage_CountObjects(page));
2145 
2146   // FPDFTextObj_GetFontName() positive testing.
2147   FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0);
2148   unsigned long size = FPDFTextObj_GetFontName(text, nullptr, 0);
2149   const char kExpectedFontName[] = "Liberation Serif";
2150   ASSERT_EQ(sizeof(kExpectedFontName), size);
2151   std::vector<char> font_name(size);
2152   ASSERT_EQ(size, FPDFTextObj_GetFontName(text, font_name.data(), size));
2153   ASSERT_STREQ(kExpectedFontName, font_name.data());
2154 
2155   // FPDFTextObj_GetFontName() negative testing.
2156   ASSERT_EQ(0U, FPDFTextObj_GetFontName(nullptr, nullptr, 0));
2157 
2158   font_name.resize(2);
2159   font_name[0] = 'x';
2160   font_name[1] = '\0';
2161   size = FPDFTextObj_GetFontName(text, font_name.data(), font_name.size());
2162   ASSERT_EQ(sizeof(kExpectedFontName), size);
2163   ASSERT_EQ(std::string("x"), std::string(font_name.data()));
2164 
2165   UnloadPage(page);
2166 }
2167 
TEST_F(FPDFEditEmbedderTest,TestFormGetObjects)2168 TEST_F(FPDFEditEmbedderTest, TestFormGetObjects) {
2169   EXPECT_TRUE(OpenDocument("form_object.pdf"));
2170   FPDF_PAGE page = LoadPage(0);
2171   ASSERT_TRUE(page);
2172   ASSERT_EQ(1, FPDFPage_CountObjects(page));
2173 
2174   FPDF_PAGEOBJECT form = FPDFPage_GetObject(page, 0);
2175   EXPECT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
2176   ASSERT_EQ(-1, FPDFFormObj_CountObjects(nullptr));
2177   ASSERT_EQ(2, FPDFFormObj_CountObjects(form));
2178 
2179   // FPDFFormObj_GetObject() positive testing.
2180   FPDF_PAGEOBJECT text1 = FPDFFormObj_GetObject(form, 0);
2181   ASSERT_TRUE(text1);
2182   float left = 0;
2183   float bottom = 0;
2184   float right = 0;
2185   float top = 0;
2186   ASSERT_TRUE(FPDFPageObj_GetBounds(text1, &left, &bottom, &right, &top));
2187   ASSERT_EQ(271, static_cast<int>(top));
2188 
2189   FPDF_PAGEOBJECT text2 = FPDFFormObj_GetObject(form, 1);
2190   ASSERT_TRUE(text2);
2191   ASSERT_TRUE(FPDFPageObj_GetBounds(text2, &left, &bottom, &right, &top));
2192   ASSERT_EQ(221, static_cast<int>(top));
2193 
2194   // FPDFFormObj_GetObject() negative testing.
2195   ASSERT_EQ(nullptr, FPDFFormObj_GetObject(nullptr, 0));
2196   ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, -1));
2197   ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, 2));
2198 
2199   // Reset the form object matrix to identity.
2200   auto* pPageObj = CPDFPageObjectFromFPDFPageObject(form);
2201   CPDF_FormObject* pFormObj = pPageObj->AsForm();
2202   pFormObj->Transform(pFormObj->form_matrix().GetInverse());
2203 
2204   // FPDFFormObj_GetMatrix() positive testing.
2205   static constexpr FS_MATRIX kMatrix = {1.0f, 1.5f, 2.0f, 2.5f, 100.0f, 200.0f};
2206   pFormObj->Transform(CFXMatrixFromFSMatrix(kMatrix));
2207 
2208   FS_MATRIX matrix;
2209   EXPECT_TRUE(FPDFFormObj_GetMatrix(form, &matrix));
2210   EXPECT_FLOAT_EQ(kMatrix.a, matrix.a);
2211   EXPECT_FLOAT_EQ(kMatrix.b, matrix.b);
2212   EXPECT_FLOAT_EQ(kMatrix.c, matrix.c);
2213   EXPECT_FLOAT_EQ(kMatrix.d, matrix.d);
2214   EXPECT_FLOAT_EQ(kMatrix.e, matrix.e);
2215   EXPECT_FLOAT_EQ(kMatrix.f, matrix.f);
2216 
2217   // FPDFFormObj_GetMatrix() negative testing.
2218   EXPECT_FALSE(FPDFFormObj_GetMatrix(nullptr, &matrix));
2219   EXPECT_FALSE(FPDFFormObj_GetMatrix(form, nullptr));
2220   EXPECT_FALSE(FPDFFormObj_GetMatrix(nullptr, nullptr));
2221 
2222   UnloadPage(page);
2223 }
2224 
2225 // Tests adding text from standard font using FPDFText_LoadStandardFont.
2226 // TODO(crbug.com/pdfium/11): Fix this test and enable.
2227 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
2228 #define MAYBE_AddStandardFontText2 DISABLED_AddStandardFontText2
2229 #else
2230 #define MAYBE_AddStandardFontText2 AddStandardFontText2
2231 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_AddStandardFontText2)2232 TEST_F(FPDFEditEmbedderTest, MAYBE_AddStandardFontText2) {
2233   // Start with a blank page
2234   ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
2235 
2236   // Load a standard font.
2237   ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), "Helvetica"));
2238   ASSERT_TRUE(font);
2239 
2240   // Add some text to the page.
2241   FPDF_PAGEOBJECT text_object =
2242       FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
2243   EXPECT_TRUE(text_object);
2244   ScopedFPDFWideString text =
2245       GetFPDFWideString(L"I'm at the bottom of the page");
2246   EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
2247   FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 20, 20);
2248   FPDFPage_InsertObject(page.get(), text_object);
2249   ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2250 #if defined(OS_MACOSX)
2251   const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
2252 #elif defined(OS_WIN)
2253   const char md5[] = "08d1ff3e5a42801bee6077fd366bef00";
2254 #else
2255   const char md5[] = "eacaa24573b8ce997b3882595f096f00";
2256 #endif
2257   CompareBitmap(page_bitmap.get(), 612, 792, md5);
2258 }
2259 
TEST_F(FPDFEditEmbedderTest,LoadStandardFonts)2260 TEST_F(FPDFEditEmbedderTest, LoadStandardFonts) {
2261   CreateNewDocument();
2262   static constexpr const char* kStandardFontNames[] = {
2263       "Arial",
2264       "Arial-Bold",
2265       "Arial-BoldItalic",
2266       "Arial-Italic",
2267       "Courier",
2268       "Courier-BoldOblique",
2269       "Courier-Oblique",
2270       "Courier-Bold",
2271       "CourierNew",
2272       "CourierNew-Bold",
2273       "CourierNew-BoldItalic",
2274       "CourierNew-Italic",
2275       "Helvetica",
2276       "Helvetica-Bold",
2277       "Helvetica-BoldOblique",
2278       "Helvetica-Oblique",
2279       "Symbol",
2280       "TimesNewRoman",
2281       "TimesNewRoman-Bold",
2282       "TimesNewRoman-BoldItalic",
2283       "TimesNewRoman-Italic",
2284       "ZapfDingbats"};
2285   for (const char* font_name : kStandardFontNames) {
2286     ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), font_name));
2287     EXPECT_TRUE(font) << font_name << " should be considered a standard font.";
2288   }
2289   static constexpr const char* kNotStandardFontNames[] = {
2290       "Abcdefg",      "ArialB",    "Arial-Style",
2291       "Font Name",    "FontArial", "NotAStandardFontName",
2292       "TestFontName", "Quack",     "Symbol-Italic",
2293       "Zapf"};
2294   for (const char* font_name : kNotStandardFontNames) {
2295     ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), font_name));
2296     EXPECT_FALSE(font) << font_name
2297                        << " should not be considered a standard font.";
2298   }
2299 }
2300 
TEST_F(FPDFEditEmbedderTest,GraphicsData)2301 TEST_F(FPDFEditEmbedderTest, GraphicsData) {
2302   // New page
2303   ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
2304 
2305   // Create a rect with nontrivial graphics
2306   FPDF_PAGEOBJECT rect1 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
2307   FPDFPageObj_SetBlendMode(rect1, "Color");
2308   FPDFPage_InsertObject(page.get(), rect1);
2309   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2310 
2311   // Check that the ExtGState was created
2312   CPDF_Page* cpage = CPDFPageFromFPDFPage(page.get());
2313   CPDF_Dictionary* graphics_dict = cpage->m_pResources->GetDictFor("ExtGState");
2314   ASSERT_TRUE(graphics_dict);
2315   EXPECT_EQ(2u, graphics_dict->size());
2316 
2317   // Add a text object causing no change to the graphics dictionary
2318   FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
2319   // Only alpha, the last component, matters for the graphics dictionary. And
2320   // the default value is 255.
2321   EXPECT_TRUE(FPDFPageObj_SetFillColor(text1, 100, 100, 100, 255));
2322   FPDFPage_InsertObject(page.get(), text1);
2323   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2324   EXPECT_EQ(2u, graphics_dict->size());
2325 
2326   // Add a text object increasing the size of the graphics dictionary
2327   FPDF_PAGEOBJECT text2 =
2328       FPDFPageObj_NewTextObj(document(), "Times-Roman", 12.0f);
2329   FPDFPage_InsertObject(page.get(), text2);
2330   FPDFPageObj_SetBlendMode(text2, "Darken");
2331   EXPECT_TRUE(FPDFPageObj_SetFillColor(text2, 0, 0, 255, 150));
2332   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2333   EXPECT_EQ(3u, graphics_dict->size());
2334 
2335   // Add a path that should reuse graphics
2336   FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
2337   FPDFPageObj_SetBlendMode(path, "Darken");
2338   EXPECT_TRUE(FPDFPageObj_SetFillColor(path, 200, 200, 100, 150));
2339   FPDFPage_InsertObject(page.get(), path);
2340   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2341   EXPECT_EQ(3u, graphics_dict->size());
2342 
2343   // Add a rect increasing the size of the graphics dictionary
2344   FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
2345   FPDFPageObj_SetBlendMode(rect2, "Darken");
2346   EXPECT_TRUE(FPDFPageObj_SetFillColor(rect2, 0, 0, 255, 150));
2347   EXPECT_TRUE(FPDFPageObj_SetStrokeColor(rect2, 0, 0, 0, 200));
2348   FPDFPage_InsertObject(page.get(), rect2);
2349   EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2350   EXPECT_EQ(4u, graphics_dict->size());
2351 }
2352 
TEST_F(FPDFEditEmbedderTest,DoubleGenerating)2353 TEST_F(FPDFEditEmbedderTest, DoubleGenerating) {
2354   // Start with a blank page
2355   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
2356 
2357   // Add a red rectangle with some non-default alpha
2358   FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
2359   EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 255, 0, 0, 128));
2360   EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
2361   FPDFPage_InsertObject(page, rect);
2362   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2363 
2364   // Check the ExtGState
2365   CPDF_Page* cpage = CPDFPageFromFPDFPage(page);
2366   CPDF_Dictionary* graphics_dict = cpage->m_pResources->GetDictFor("ExtGState");
2367   ASSERT_TRUE(graphics_dict);
2368   EXPECT_EQ(2u, graphics_dict->size());
2369 
2370   // Check the bitmap
2371   {
2372     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2373     CompareBitmap(page_bitmap.get(), 612, 792,
2374                   "5384da3406d62360ffb5cac4476fff1c");
2375   }
2376 
2377   // Never mind, my new favorite color is blue, increase alpha
2378   EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 0, 0, 255, 180));
2379   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2380   EXPECT_EQ(3u, graphics_dict->size());
2381 
2382   // Check that bitmap displays changed content
2383   {
2384     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2385     CompareBitmap(page_bitmap.get(), 612, 792,
2386                   "2e51656f5073b0bee611d9cd086aa09c");
2387   }
2388 
2389   // And now generate, without changes
2390   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2391   EXPECT_EQ(3u, graphics_dict->size());
2392   {
2393     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2394     CompareBitmap(page_bitmap.get(), 612, 792,
2395                   "2e51656f5073b0bee611d9cd086aa09c");
2396   }
2397 
2398   // Add some text to the page
2399   FPDF_PAGEOBJECT text_object =
2400       FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
2401   ScopedFPDFWideString text =
2402       GetFPDFWideString(L"Something something #text# something");
2403   EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
2404   FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300);
2405   FPDFPage_InsertObject(page, text_object);
2406   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2407   CPDF_Dictionary* font_dict = cpage->m_pResources->GetDictFor("Font");
2408   ASSERT_TRUE(font_dict);
2409   EXPECT_EQ(1u, font_dict->size());
2410 
2411   // Generate yet again, check dicts are reasonably sized
2412   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2413   EXPECT_EQ(3u, graphics_dict->size());
2414   EXPECT_EQ(1u, font_dict->size());
2415   FPDF_ClosePage(page);
2416 }
2417 
TEST_F(FPDFEditEmbedderTest,LoadSimpleType1Font)2418 TEST_F(FPDFEditEmbedderTest, LoadSimpleType1Font) {
2419   CreateNewDocument();
2420   // TODO(npm): use other fonts after disallowing loading any font as any type
2421   RetainPtr<CPDF_Font> stock_font =
2422       CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
2423   pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
2424   ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
2425                                         FPDF_FONT_TYPE1, false));
2426   ASSERT_TRUE(font.get());
2427   CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
2428   EXPECT_TRUE(typed_font->IsType1Font());
2429 
2430   const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
2431   EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
2432   EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
2433   EXPECT_EQ("TimesNewRomanPS-BoldMT", font_dict->GetStringFor("BaseFont"));
2434   ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
2435   ASSERT_TRUE(font_dict->KeyExist("LastChar"));
2436   EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
2437   EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
2438 
2439   const CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
2440   ASSERT_TRUE(widths_array);
2441   ASSERT_EQ(224u, widths_array->size());
2442   EXPECT_EQ(250, widths_array->GetNumberAt(0));
2443   EXPECT_EQ(569, widths_array->GetNumberAt(11));
2444   EXPECT_EQ(500, widths_array->GetNumberAt(223));
2445   CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, span);
2446 }
2447 
TEST_F(FPDFEditEmbedderTest,LoadSimpleTrueTypeFont)2448 TEST_F(FPDFEditEmbedderTest, LoadSimpleTrueTypeFont) {
2449   CreateNewDocument();
2450   RetainPtr<CPDF_Font> stock_font =
2451       CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
2452   pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
2453   ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
2454                                         FPDF_FONT_TRUETYPE, false));
2455   ASSERT_TRUE(font.get());
2456   CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
2457   EXPECT_TRUE(typed_font->IsTrueTypeFont());
2458 
2459   const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
2460   EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
2461   EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
2462   EXPECT_EQ("CourierNewPSMT", font_dict->GetStringFor("BaseFont"));
2463   ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
2464   ASSERT_TRUE(font_dict->KeyExist("LastChar"));
2465   EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
2466   EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
2467 
2468   const CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
2469   ASSERT_TRUE(widths_array);
2470   ASSERT_EQ(224u, widths_array->size());
2471   EXPECT_EQ(600, widths_array->GetNumberAt(33));
2472   EXPECT_EQ(600, widths_array->GetNumberAt(74));
2473   EXPECT_EQ(600, widths_array->GetNumberAt(223));
2474   CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, span);
2475 }
2476 
TEST_F(FPDFEditEmbedderTest,LoadCIDType0Font)2477 TEST_F(FPDFEditEmbedderTest, LoadCIDType0Font) {
2478   CreateNewDocument();
2479   RetainPtr<CPDF_Font> stock_font =
2480       CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
2481   pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
2482   ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
2483                                         FPDF_FONT_TYPE1, 1));
2484   ASSERT_TRUE(font.get());
2485   CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
2486   EXPECT_TRUE(typed_font->IsCIDFont());
2487 
2488   // Check font dictionary entries
2489   const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
2490   EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
2491   EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
2492   EXPECT_EQ("TimesNewRomanPSMT-Identity-H",
2493             font_dict->GetStringFor("BaseFont"));
2494   EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
2495   const CPDF_Array* descendant_array =
2496       font_dict->GetArrayFor("DescendantFonts");
2497   ASSERT_TRUE(descendant_array);
2498   EXPECT_EQ(1u, descendant_array->size());
2499 
2500   // Check the CIDFontDict
2501   const CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
2502   EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
2503   EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
2504   EXPECT_EQ("TimesNewRomanPSMT", cidfont_dict->GetStringFor("BaseFont"));
2505   const CPDF_Dictionary* cidinfo_dict =
2506       cidfont_dict->GetDictFor("CIDSystemInfo");
2507   ASSERT_TRUE(cidinfo_dict);
2508   const CPDF_Object* registry = cidinfo_dict->GetObjectFor("Registry");
2509   ASSERT_TRUE(registry);
2510   EXPECT_EQ(CPDF_Object::kString, registry->GetType());
2511   EXPECT_EQ("Adobe", registry->GetString());
2512   const CPDF_Object* ordering = cidinfo_dict->GetObjectFor("Ordering");
2513   ASSERT_TRUE(ordering);
2514   EXPECT_EQ(CPDF_Object::kString, ordering->GetType());
2515   EXPECT_EQ("Identity", ordering->GetString());
2516   EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
2517   CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, span);
2518 
2519   // Check widths
2520   const CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
2521   ASSERT_TRUE(widths_array);
2522   EXPECT_GT(widths_array->size(), 1u);
2523   CheckCompositeFontWidths(widths_array, typed_font);
2524 }
2525 
TEST_F(FPDFEditEmbedderTest,LoadCIDType2Font)2526 TEST_F(FPDFEditEmbedderTest, LoadCIDType2Font) {
2527   CreateNewDocument();
2528   RetainPtr<CPDF_Font> stock_font =
2529       CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
2530   pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
2531   ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
2532                                         FPDF_FONT_TRUETYPE, 1));
2533   ASSERT_TRUE(font.get());
2534   CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
2535   EXPECT_TRUE(typed_font->IsCIDFont());
2536 
2537   // Check font dictionary entries
2538   const CPDF_Dictionary* font_dict = typed_font->GetFontDict();
2539   EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
2540   EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
2541   EXPECT_EQ("Arial-ItalicMT", font_dict->GetStringFor("BaseFont"));
2542   EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
2543   const CPDF_Array* descendant_array =
2544       font_dict->GetArrayFor("DescendantFonts");
2545   ASSERT_TRUE(descendant_array);
2546   EXPECT_EQ(1u, descendant_array->size());
2547 
2548   // Check the CIDFontDict
2549   const CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
2550   EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
2551   EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
2552   EXPECT_EQ("Arial-ItalicMT", cidfont_dict->GetStringFor("BaseFont"));
2553   const CPDF_Dictionary* cidinfo_dict =
2554       cidfont_dict->GetDictFor("CIDSystemInfo");
2555   ASSERT_TRUE(cidinfo_dict);
2556   EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
2557   EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
2558   EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
2559   CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, span);
2560 
2561   // Check widths
2562   const CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
2563   ASSERT_TRUE(widths_array);
2564   CheckCompositeFontWidths(widths_array, typed_font);
2565 }
2566 
TEST_F(FPDFEditEmbedderTest,NormalizeNegativeRotation)2567 TEST_F(FPDFEditEmbedderTest, NormalizeNegativeRotation) {
2568   // Load document with a -90 degree rotation
2569   EXPECT_TRUE(OpenDocument("bug_713197.pdf"));
2570   FPDF_PAGE page = LoadPage(0);
2571   EXPECT_NE(nullptr, page);
2572 
2573   EXPECT_EQ(3, FPDFPage_GetRotation(page));
2574   UnloadPage(page);
2575 }
2576 
2577 // TODO(crbug.com/pdfium/11): Fix this test and enable.
2578 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
2579 #define MAYBE_AddTrueTypeFontText DISABLED_AddTrueTypeFontText
2580 #else
2581 #define MAYBE_AddTrueTypeFontText AddTrueTypeFontText
2582 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_AddTrueTypeFontText)2583 TEST_F(FPDFEditEmbedderTest, MAYBE_AddTrueTypeFontText) {
2584   // Start with a blank page
2585   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
2586   {
2587     RetainPtr<CPDF_Font> stock_font =
2588         CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
2589     pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
2590     ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
2591                                           FPDF_FONT_TRUETYPE, 0));
2592     ASSERT_TRUE(font.get());
2593 
2594     // Add some text to the page
2595     FPDF_PAGEOBJECT text_object =
2596         FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
2597     EXPECT_TRUE(text_object);
2598     ScopedFPDFWideString text =
2599         GetFPDFWideString(L"I am testing my loaded font, WEE.");
2600     EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
2601     FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
2602     FPDFPage_InsertObject(page, text_object);
2603     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2604 #if defined(OS_MACOSX)
2605     const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
2606 #elif defined(OS_WIN)
2607     const char md5[] = "d60ba39f9698e32360d99e727dd93165";
2608 #else
2609     const char md5[] = "70592859010ffbf532a2237b8118bcc4";
2610 #endif
2611     CompareBitmap(page_bitmap.get(), 612, 792, md5);
2612 
2613     // Add some more text, same font
2614     FPDF_PAGEOBJECT text_object2 =
2615         FPDFPageObj_CreateTextObj(document(), font.get(), 15.0f);
2616     ScopedFPDFWideString text2 = GetFPDFWideString(L"Bigger font size");
2617     EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
2618     FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 200, 200);
2619     FPDFPage_InsertObject(page, text_object2);
2620   }
2621   ScopedFPDFBitmap page_bitmap2 = RenderPage(page);
2622 #if defined(OS_MACOSX)
2623   const char md5_2[] = "8eded4193ff1f0f77b8b600a825e97ea";
2624 #elif defined(OS_WIN)
2625   const char md5_2[] = "2199b579c49ab5f80c246a586a80ee90";
2626 #else
2627   const char md5_2[] = "c1d10cce1761c4a998a16b2562030568";
2628 #endif
2629   CompareBitmap(page_bitmap2.get(), 612, 792, md5_2);
2630 
2631   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2632   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2633   FPDF_ClosePage(page);
2634 
2635   VerifySavedDocument(612, 792, md5_2);
2636 }
2637 
TEST_F(FPDFEditEmbedderTest,TransformAnnot)2638 TEST_F(FPDFEditEmbedderTest, TransformAnnot) {
2639   // Open a file with one annotation and load its first page.
2640   ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
2641   FPDF_PAGE page = LoadPage(0);
2642   ASSERT_TRUE(page);
2643 
2644   {
2645     // Add an underline annotation to the page without specifying its rectangle.
2646     ScopedFPDFAnnotation annot(
2647         FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE));
2648     ASSERT_TRUE(annot);
2649 
2650     // FPDFPage_TransformAnnots() should run without errors when modifying
2651     // annotation rectangles.
2652     FPDFPage_TransformAnnots(page, 1, 2, 3, 4, 5, 6);
2653   }
2654   UnloadPage(page);
2655 }
2656 
2657 // TODO(npm): Add tests using Japanese fonts in other OS.
2658 #if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
2659 // TODO(crbug.com/pdfium/11): Fix this test and enable.
2660 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
2661 #define MAYBE_AddCIDFontText DISABLED_AddCIDFontText
2662 #else
2663 #define MAYBE_AddCIDFontText AddCIDFontText
2664 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_AddCIDFontText)2665 TEST_F(FPDFEditEmbedderTest, MAYBE_AddCIDFontText) {
2666   // Start with a blank page
2667   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
2668   CFX_Font CIDfont;
2669   {
2670     // First, get the data from the font
2671     CIDfont.LoadSubst("IPAGothic", 1, 0, 400, 0, 932, 0);
2672     EXPECT_EQ("IPAGothic", CIDfont.GetFaceName());
2673     pdfium::span<const uint8_t> span = CIDfont.GetFontSpan();
2674 
2675     // Load the data into a FPDF_Font.
2676     ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
2677                                           FPDF_FONT_TRUETYPE, 1));
2678     ASSERT_TRUE(font.get());
2679 
2680     // Add some text to the page
2681     FPDF_PAGEOBJECT text_object =
2682         FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
2683     ASSERT_TRUE(text_object);
2684     std::wstring wstr = L"ABCDEFGhijklmnop.";
2685     ScopedFPDFWideString text = GetFPDFWideString(wstr);
2686     EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
2687     FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 200);
2688     FPDFPage_InsertObject(page, text_object);
2689 
2690     // And add some Japanese characters
2691     FPDF_PAGEOBJECT text_object2 =
2692         FPDFPageObj_CreateTextObj(document(), font.get(), 18.0f);
2693     ASSERT_TRUE(text_object2);
2694     std::wstring wstr2 =
2695         L"\u3053\u3093\u306B\u3061\u306f\u4e16\u754C\u3002\u3053\u3053\u306B1"
2696         L"\u756A";
2697     ScopedFPDFWideString text2 = GetFPDFWideString(wstr2);
2698     EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
2699     FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 500);
2700     FPDFPage_InsertObject(page, text_object2);
2701   }
2702 
2703   // Check that the text renders properly.
2704   const char md5[] = "5159a72903fe57bf0cf645c894de8a74";
2705   {
2706     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2707     CompareBitmap(page_bitmap.get(), 612, 792, md5);
2708   }
2709 
2710   // Save the document, close the page.
2711   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2712   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2713   FPDF_ClosePage(page);
2714 
2715   VerifySavedDocument(612, 792, md5);
2716 }
2717 #endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
2718 
2719 // TODO(crbug.com/pdfium/11): Fix this test and enable.
2720 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
2721 #define MAYBE_SaveAndRender DISABLED_SaveAndRender
2722 #else
2723 #define MAYBE_SaveAndRender SaveAndRender
2724 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_SaveAndRender)2725 TEST_F(FPDFEditEmbedderTest, MAYBE_SaveAndRender) {
2726   const char md5[] = "3c20472b0552c0c22b88ab1ed8c6202b";
2727   {
2728     EXPECT_TRUE(OpenDocument("bug_779.pdf"));
2729     FPDF_PAGE page = LoadPage(0);
2730     ASSERT_NE(nullptr, page);
2731 
2732     // Now add a more complex blue path.
2733     FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20);
2734     EXPECT_TRUE(FPDFPageObj_SetFillColor(green_path, 0, 255, 0, 200));
2735     // TODO(npm): stroking will cause the MD5s to differ.
2736     EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0));
2737     EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63));
2738     EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90));
2739     EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 133));
2740     EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 33));
2741     EXPECT_TRUE(FPDFPath_BezierTo(green_path, 38, 33, 39, 36, 40, 40));
2742     EXPECT_TRUE(FPDFPath_Close(green_path));
2743     FPDFPage_InsertObject(page, green_path);
2744     ScopedFPDFBitmap page_bitmap = RenderLoadedPage(page);
2745     CompareBitmap(page_bitmap.get(), 612, 792, md5);
2746 
2747     // Now save the result, closing the page and document
2748     EXPECT_TRUE(FPDFPage_GenerateContent(page));
2749     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2750     UnloadPage(page);
2751   }
2752 
2753   VerifySavedDocument(612, 792, md5);
2754 }
2755 
TEST_F(FPDFEditEmbedderTest,AddMark)2756 TEST_F(FPDFEditEmbedderTest, AddMark) {
2757   // Load document with some text.
2758   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
2759   FPDF_PAGE page = LoadPage(0);
2760   ASSERT_TRUE(page);
2761 
2762   CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
2763 
2764   // Add to the first page object a "Bounds" mark with "Position": "First".
2765   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
2766   FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(page_object, "Bounds");
2767   EXPECT_TRUE(mark);
2768   EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
2769                                              "Position", "First"));
2770 
2771   CheckMarkCounts(page, 1, 19, 8, 4, 9, 2);
2772 
2773   // Save the file
2774   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2775   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2776   UnloadPage(page);
2777 
2778   // Re-open the file and check the new mark is present.
2779   ASSERT_TRUE(OpenSavedDocument());
2780   FPDF_PAGE saved_page = LoadSavedPage(0);
2781 
2782   CheckMarkCounts(saved_page, 1, 19, 8, 4, 9, 2);
2783 
2784   CloseSavedPage(saved_page);
2785   CloseSavedDocument();
2786 }
2787 
2788 // TODO(crbug.com/pdfium/11): Fix this test and enable.
2789 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
2790 #define MAYBE_AddMarkCompressedStream DISABLED_AddMarkCompressedStream
2791 #else
2792 #define MAYBE_AddMarkCompressedStream AddMarkCompressedStream
2793 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_AddMarkCompressedStream)2794 TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkCompressedStream) {
2795 #if defined(OS_MACOSX)
2796   const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a";
2797 #elif defined(OS_WIN)
2798   const char kOriginalMD5[] = "795b7ce1626931aa06af0fa23b7d80bb";
2799 #else
2800   const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed";
2801 #endif
2802 
2803   // Load document with some text in a compressed stream.
2804   EXPECT_TRUE(OpenDocument("hello_world_compressed_stream.pdf"));
2805   FPDF_PAGE page = LoadPage(0);
2806   ASSERT_TRUE(page);
2807 
2808   // Render and check there are no marks.
2809   {
2810     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2811     CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
2812   }
2813   CheckMarkCounts(page, 0, 2, 0, 0, 0, 0);
2814 
2815   // Add to the first page object a "Bounds" mark with "Position": "First".
2816   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0);
2817   FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(page_object, "Bounds");
2818   EXPECT_TRUE(mark);
2819   EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
2820                                              "Position", "First"));
2821 
2822   // Render and check there is 1 mark.
2823   {
2824     ScopedFPDFBitmap page_bitmap = RenderPage(page);
2825     CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
2826   }
2827   CheckMarkCounts(page, 0, 2, 0, 0, 0, 1);
2828 
2829   // Save the file.
2830   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2831   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2832   UnloadPage(page);
2833 
2834   // Re-open the file and check the new mark is present.
2835   ASSERT_TRUE(OpenSavedDocument());
2836   FPDF_PAGE saved_page = LoadSavedPage(0);
2837 
2838   {
2839     ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2840     CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
2841   }
2842   CheckMarkCounts(saved_page, 0, 2, 0, 0, 0, 1);
2843 
2844   CloseSavedPage(saved_page);
2845   CloseSavedDocument();
2846 }
2847 
TEST_F(FPDFEditEmbedderTest,SetMarkParam)2848 TEST_F(FPDFEditEmbedderTest, SetMarkParam) {
2849   // Load document with some text.
2850   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
2851   FPDF_PAGE page = LoadPage(0);
2852   ASSERT_TRUE(page);
2853 
2854   constexpr int kExpectedObjectCount = 19;
2855   CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
2856 
2857   // Check the "Bounds" mark's "Position" param is "Last".
2858   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
2859   FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
2860   ASSERT_TRUE(mark);
2861   char buffer[256];
2862   unsigned long name_len = 999u;
2863   ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
2864   EXPECT_EQ((6u + 1u) * 2u, name_len);
2865   ASSERT_EQ(L"Bounds",
2866             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
2867   unsigned long out_buffer_len;
2868   ASSERT_TRUE(FPDFPageObjMark_GetParamStringValue(
2869       mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
2870   ASSERT_EQ(L"Last",
2871             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
2872 
2873   // Set is to "End".
2874   EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
2875                                              "Position", "End"));
2876 
2877   // Verify the object passed must correspond to the mark passed.
2878   FPDF_PAGEOBJECT another_page_object = FPDFPage_GetObject(page, 17);
2879   EXPECT_FALSE(FPDFPageObjMark_SetStringParam(document(), another_page_object,
2880                                               mark, "Position", "End"));
2881 
2882   // Verify nothing else changed.
2883   CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
2884 
2885   // Verify "Position" now maps to "End".
2886   EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
2887       mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
2888   EXPECT_EQ(L"End",
2889             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
2890 
2891   // Save the file
2892   EXPECT_TRUE(FPDFPage_GenerateContent(page));
2893   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2894   UnloadPage(page);
2895 
2896   // Re-open the file and cerify "Position" still maps to "End".
2897   ASSERT_TRUE(OpenSavedDocument());
2898   FPDF_PAGE saved_page = LoadSavedPage(0);
2899 
2900   CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 8, 4, 9, 1);
2901   page_object = FPDFPage_GetObject(saved_page, 18);
2902   mark = FPDFPageObj_GetMark(page_object, 1);
2903   EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
2904       mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
2905   EXPECT_EQ(L"End",
2906             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
2907 
2908   CloseSavedPage(saved_page);
2909   CloseSavedDocument();
2910 }
2911 
2912 // TODO(crbug.com/pdfium/11): Fix this test and enable.
2913 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
2914 #define MAYBE_AddMarkedText DISABLED_AddMarkedText
2915 #else
2916 #define MAYBE_AddMarkedText AddMarkedText
2917 #endif
TEST_F(FPDFEditEmbedderTest,MAYBE_AddMarkedText)2918 TEST_F(FPDFEditEmbedderTest, MAYBE_AddMarkedText) {
2919   // Start with a blank page.
2920   FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
2921 
2922   RetainPtr<CPDF_Font> stock_font =
2923       CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
2924   pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
2925   ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
2926                                         FPDF_FONT_TRUETYPE, 0));
2927   ASSERT_TRUE(font.get());
2928 
2929   // Add some text to the page.
2930   FPDF_PAGEOBJECT text_object =
2931       FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
2932 
2933   EXPECT_TRUE(text_object);
2934   ScopedFPDFWideString text1 =
2935       GetFPDFWideString(L"I am testing my loaded font, WEE.");
2936   EXPECT_TRUE(FPDFText_SetText(text_object, text1.get()));
2937   FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
2938   FPDFPage_InsertObject(page, text_object);
2939 
2940   // Add a mark with the tag "TestMarkName" to that text.
2941   EXPECT_EQ(0, FPDFPageObj_CountMarks(text_object));
2942   FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(text_object, "Test Mark Name");
2943   EXPECT_TRUE(mark);
2944   EXPECT_EQ(1, FPDFPageObj_CountMarks(text_object));
2945   EXPECT_EQ(mark, FPDFPageObj_GetMark(text_object, 0));
2946   char buffer[256];
2947   unsigned long name_len = 999u;
2948   ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
2949   EXPECT_EQ((14u + 1u) * 2, name_len);
2950   std::wstring name =
2951       GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
2952   EXPECT_EQ(L"Test Mark Name", name);
2953 
2954   // Add parameters:
2955   // - int "IntKey" : 42
2956   // - string "StringKey": "StringValue"
2957   // - blob "BlobKey": "\x01\x02\x03\0BlobValue1\0\0\0BlobValue2\0"
2958   constexpr size_t kBlobLen = 28;
2959   char block_value[kBlobLen];
2960   memcpy(block_value, "\x01\x02\x03\0BlobValue1\0\0\0BlobValue2\0", kBlobLen);
2961   EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark));
2962   EXPECT_TRUE(
2963       FPDFPageObjMark_SetIntParam(document(), text_object, mark, "IntKey", 42));
2964   EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), text_object, mark,
2965                                              "StringKey", "StringValue"));
2966   EXPECT_TRUE(FPDFPageObjMark_SetBlobParam(document(), text_object, mark,
2967                                            "BlobKey", block_value, kBlobLen));
2968   EXPECT_EQ(3, FPDFPageObjMark_CountParams(mark));
2969 
2970   // Check the two parameters can be retrieved.
2971   EXPECT_EQ(FPDF_OBJECT_NUMBER,
2972             FPDFPageObjMark_GetParamValueType(mark, "IntKey"));
2973   int int_value;
2974   EXPECT_TRUE(FPDFPageObjMark_GetParamIntValue(mark, "IntKey", &int_value));
2975   EXPECT_EQ(42, int_value);
2976 
2977   EXPECT_EQ(FPDF_OBJECT_STRING,
2978             FPDFPageObjMark_GetParamValueType(mark, "StringKey"));
2979   unsigned long out_buffer_len = 999u;
2980   EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
2981       mark, "StringKey", buffer, sizeof(buffer), &out_buffer_len));
2982   EXPECT_GT(out_buffer_len, 0u);
2983   EXPECT_NE(999u, out_buffer_len);
2984   name = GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
2985   EXPECT_EQ(L"StringValue", name);
2986 
2987   EXPECT_EQ(FPDF_OBJECT_STRING,
2988             FPDFPageObjMark_GetParamValueType(mark, "BlobKey"));
2989   out_buffer_len = 0;
2990   EXPECT_TRUE(FPDFPageObjMark_GetParamBlobValue(
2991       mark, "BlobKey", buffer, sizeof(buffer), &out_buffer_len));
2992   EXPECT_EQ(kBlobLen, out_buffer_len);
2993   EXPECT_EQ(0, memcmp(block_value, buffer, kBlobLen));
2994 
2995 // Render and check the bitmap is the expected one.
2996 #if defined(OS_MACOSX)
2997   const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
2998 #elif defined(OS_WIN)
2999   const char md5[] = "d60ba39f9698e32360d99e727dd93165";
3000 #else
3001   const char md5[] = "70592859010ffbf532a2237b8118bcc4";
3002 #endif
3003   {
3004     ScopedFPDFBitmap page_bitmap = RenderPage(page);
3005     CompareBitmap(page_bitmap.get(), 612, 792, md5);
3006   }
3007 
3008   // Now save the result.
3009   EXPECT_EQ(1, FPDFPage_CountObjects(page));
3010   EXPECT_TRUE(FPDFPage_GenerateContent(page));
3011   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3012 
3013   FPDF_ClosePage(page);
3014 
3015   // Re-open the file and check the changes were kept in the saved .pdf.
3016   ASSERT_TRUE(OpenSavedDocument());
3017   FPDF_PAGE saved_page = LoadSavedPage(0);
3018   EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
3019 
3020   text_object = FPDFPage_GetObject(saved_page, 0);
3021   EXPECT_TRUE(text_object);
3022   EXPECT_EQ(1, FPDFPageObj_CountMarks(text_object));
3023   mark = FPDFPageObj_GetMark(text_object, 0);
3024   EXPECT_TRUE(mark);
3025 
3026   name_len = 999u;
3027   ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
3028   EXPECT_EQ((14u + 1u) * 2, name_len);
3029   name = GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
3030   EXPECT_EQ(L"Test Mark Name", name);
3031 
3032   CloseSavedPage(saved_page);
3033   CloseSavedDocument();
3034 }
3035 
TEST_F(FPDFEditEmbedderTest,MarkGetName)3036 TEST_F(FPDFEditEmbedderTest, MarkGetName) {
3037   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
3038   FPDF_PAGE page = LoadPage(0);
3039   ASSERT_TRUE(page);
3040   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
3041   FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
3042   ASSERT_TRUE(mark);
3043 
3044   char buffer[256];
3045   unsigned long out_len;
3046 
3047   // Show the positive cases of FPDFPageObjMark_GetName.
3048   out_len = 999u;
3049   EXPECT_TRUE(FPDFPageObjMark_GetName(mark, nullptr, 0, &out_len));
3050   EXPECT_EQ((6u + 1u) * 2u, out_len);
3051 
3052   out_len = 999u;
3053   EXPECT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &out_len));
3054   EXPECT_EQ(L"Bounds",
3055             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
3056   EXPECT_EQ((6u + 1u) * 2u, out_len);
3057 
3058   // Show the negative cases of FPDFPageObjMark_GetName.
3059   out_len = 999u;
3060   EXPECT_FALSE(
3061       FPDFPageObjMark_GetName(nullptr, buffer, sizeof(buffer), &out_len));
3062   EXPECT_EQ(999u, out_len);
3063 
3064   EXPECT_FALSE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), nullptr));
3065 
3066   UnloadPage(page);
3067 }
3068 
TEST_F(FPDFEditEmbedderTest,MarkGetParamKey)3069 TEST_F(FPDFEditEmbedderTest, MarkGetParamKey) {
3070   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
3071   FPDF_PAGE page = LoadPage(0);
3072   ASSERT_TRUE(page);
3073   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
3074   FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
3075   ASSERT_TRUE(mark);
3076 
3077   char buffer[256];
3078   unsigned long out_len;
3079 
3080   // Show the positive cases of FPDFPageObjMark_GetParamKey.
3081   out_len = 999u;
3082   EXPECT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, nullptr, 0, &out_len));
3083   EXPECT_EQ((8u + 1u) * 2u, out_len);
3084 
3085   out_len = 999u;
3086   EXPECT_TRUE(
3087       FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer), &out_len));
3088   EXPECT_EQ(L"Position",
3089             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
3090   EXPECT_EQ((8u + 1u) * 2u, out_len);
3091 
3092   // Show the negative cases of FPDFPageObjMark_GetParamKey.
3093   out_len = 999u;
3094   EXPECT_FALSE(FPDFPageObjMark_GetParamKey(nullptr, 0, buffer, sizeof(buffer),
3095                                            &out_len));
3096   EXPECT_EQ(999u, out_len);
3097 
3098   out_len = 999u;
3099   EXPECT_FALSE(
3100       FPDFPageObjMark_GetParamKey(mark, 1, buffer, sizeof(buffer), &out_len));
3101   EXPECT_EQ(999u, out_len);
3102 
3103   EXPECT_FALSE(
3104       FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer), nullptr));
3105 
3106   UnloadPage(page);
3107 }
3108 
TEST_F(FPDFEditEmbedderTest,MarkGetIntParam)3109 TEST_F(FPDFEditEmbedderTest, MarkGetIntParam) {
3110   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
3111   FPDF_PAGE page = LoadPage(0);
3112   ASSERT_TRUE(page);
3113   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 8);
3114   FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 0);
3115   ASSERT_TRUE(mark);
3116 
3117   int out_value;
3118 
3119   // Show the positive cases of FPDFPageObjMark_GetParamIntValue.
3120   out_value = 999;
3121   EXPECT_TRUE(FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
3122   EXPECT_EQ(3, out_value);
3123 
3124   // Show the negative cases of FPDFPageObjMark_GetParamIntValue.
3125   out_value = 999;
3126   EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(nullptr, "Factor", &out_value));
3127   EXPECT_EQ(999, out_value);
3128 
3129   out_value = 999;
3130   EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "ParamThatDoesNotExist",
3131                                                 &out_value));
3132   EXPECT_EQ(999, out_value);
3133 
3134   EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "Factor", nullptr));
3135 
3136   page_object = FPDFPage_GetObject(page, 18);
3137   mark = FPDFPageObj_GetMark(page_object, 1);
3138   out_value = 999;
3139   EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "Position", &out_value));
3140   EXPECT_EQ(999, out_value);
3141 
3142   UnloadPage(page);
3143 }
3144 
TEST_F(FPDFEditEmbedderTest,MarkGetStringParam)3145 TEST_F(FPDFEditEmbedderTest, MarkGetStringParam) {
3146   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
3147   FPDF_PAGE page = LoadPage(0);
3148   ASSERT_TRUE(page);
3149   FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 18);
3150   FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
3151   ASSERT_TRUE(mark);
3152 
3153   char buffer[256];
3154   unsigned long out_len;
3155 
3156   // Show the positive cases of FPDFPageObjMark_GetParamStringValue.
3157   out_len = 999u;
3158   EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(mark, "Position", nullptr, 0,
3159                                                   &out_len));
3160   EXPECT_EQ((4u + 1u) * 2u, out_len);
3161 
3162   out_len = 999u;
3163   EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(mark, "Position", buffer,
3164                                                   sizeof(buffer), &out_len));
3165   EXPECT_EQ(L"Last",
3166             GetPlatformWString(reinterpret_cast<unsigned short*>(buffer)));
3167   EXPECT_EQ((4u + 1u) * 2u, out_len);
3168 
3169   // Show the negative cases of FPDFPageObjMark_GetParamStringValue.
3170   out_len = 999u;
3171   EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(nullptr, "Position", buffer,
3172                                                    sizeof(buffer), &out_len));
3173   EXPECT_EQ(999u, out_len);
3174 
3175   out_len = 999u;
3176   EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(
3177       mark, "ParamThatDoesNotExist", buffer, sizeof(buffer), &out_len));
3178   EXPECT_EQ(999u, out_len);
3179 
3180   EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(mark, "Position", buffer,
3181                                                    sizeof(buffer), nullptr));
3182 
3183   page_object = FPDFPage_GetObject(page, 8);
3184   mark = FPDFPageObj_GetMark(page_object, 0);
3185   out_len = 999u;
3186   EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(mark, "Factor", buffer,
3187                                                    sizeof(buffer), &out_len));
3188   EXPECT_EQ(999u, out_len);
3189 
3190   UnloadPage(page);
3191 }
3192 
TEST_F(FPDFEditEmbedderTest,ExtractImageBitmap)3193 TEST_F(FPDFEditEmbedderTest, ExtractImageBitmap) {
3194   ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
3195   FPDF_PAGE page = LoadPage(0);
3196   ASSERT_TRUE(page);
3197   ASSERT_EQ(39, FPDFPage_CountObjects(page));
3198 
3199   FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 32);
3200   EXPECT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3201   EXPECT_FALSE(FPDFImageObj_GetBitmap(obj));
3202 
3203   obj = FPDFPage_GetObject(page, 33);
3204   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3205   FPDF_BITMAP bitmap = FPDFImageObj_GetBitmap(obj);
3206   EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
3207   CompareBitmap(bitmap, 109, 88, "cb3637934bb3b95a6e4ae1ea9eb9e56e");
3208   FPDFBitmap_Destroy(bitmap);
3209 
3210   obj = FPDFPage_GetObject(page, 34);
3211   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3212   bitmap = FPDFImageObj_GetBitmap(obj);
3213   EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
3214   CompareBitmap(bitmap, 103, 75, "c8d51fa6821ceb2a67f08446ff236c40");
3215   FPDFBitmap_Destroy(bitmap);
3216 
3217   obj = FPDFPage_GetObject(page, 35);
3218   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3219   bitmap = FPDFImageObj_GetBitmap(obj);
3220   EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap));
3221   CompareBitmap(bitmap, 92, 68, "9c6d76cb1e37ef8514f9455d759391f3");
3222   FPDFBitmap_Destroy(bitmap);
3223 
3224   obj = FPDFPage_GetObject(page, 36);
3225   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3226   bitmap = FPDFImageObj_GetBitmap(obj);
3227   EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
3228   CompareBitmap(bitmap, 79, 60, "f4e72fb783a01c7b4614cdc25eaa98ac");
3229   FPDFBitmap_Destroy(bitmap);
3230 
3231   obj = FPDFPage_GetObject(page, 37);
3232   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3233   bitmap = FPDFImageObj_GetBitmap(obj);
3234   EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
3235   CompareBitmap(bitmap, 126, 106, "2cf9e66414c72461f4ccbf9cdebdfa1b");
3236   FPDFBitmap_Destroy(bitmap);
3237 
3238   obj = FPDFPage_GetObject(page, 38);
3239   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3240   bitmap = FPDFImageObj_GetBitmap(obj);
3241   EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
3242   CompareBitmap(bitmap, 194, 119, "a8f3a126cec274dab8242fd2ccdc1b8b");
3243   FPDFBitmap_Destroy(bitmap);
3244   UnloadPage(page);
3245 }
3246 
TEST_F(FPDFEditEmbedderTest,ExtractJBigImageBitmap)3247 TEST_F(FPDFEditEmbedderTest, ExtractJBigImageBitmap) {
3248   ASSERT_TRUE(OpenDocument("bug_631912.pdf"));
3249   FPDF_PAGE page = LoadPage(0);
3250   ASSERT_TRUE(page);
3251   ASSERT_EQ(1, FPDFPage_CountObjects(page));
3252 
3253   FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 0);
3254   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3255   {
3256     ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
3257     ASSERT_TRUE(bitmap);
3258     EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap.get()));
3259     CompareBitmap(bitmap.get(), 1152, 720, "3f6a48e2b3e91b799bf34567f55cb4de");
3260   }
3261 
3262   UnloadPage(page);
3263 }
3264 
TEST_F(FPDFEditEmbedderTest,GetImageData)3265 TEST_F(FPDFEditEmbedderTest, GetImageData) {
3266   EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
3267   FPDF_PAGE page = LoadPage(0);
3268   ASSERT_TRUE(page);
3269   ASSERT_EQ(39, FPDFPage_CountObjects(page));
3270 
3271   // Retrieve an image object with flate-encoded data stream.
3272   FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33);
3273   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3274 
3275   // Check that the raw image data has the correct length and hash value.
3276   unsigned long len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
3277   std::vector<char> buf(len);
3278   EXPECT_EQ(4091u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
3279   EXPECT_EQ("f73802327d2e88e890f653961bcda81a",
3280             GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
3281 
3282   // Check that the decoded image data has the correct length and hash value.
3283   len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
3284   buf.clear();
3285   buf.resize(len);
3286   EXPECT_EQ(28776u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
3287   EXPECT_EQ("cb3637934bb3b95a6e4ae1ea9eb9e56e",
3288             GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
3289 
3290   // Retrieve an image object with DCTDecode-encoded data stream.
3291   obj = FPDFPage_GetObject(page, 37);
3292   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3293 
3294   // Check that the raw image data has the correct length and hash value.
3295   len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
3296   buf.clear();
3297   buf.resize(len);
3298   EXPECT_EQ(4370u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
3299   EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
3300             GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
3301 
3302   // Check that the decoded image data has the correct length and hash value,
3303   // which should be the same as those of the raw data, since this image is
3304   // encoded by a single DCTDecode filter and decoding is a noop.
3305   len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
3306   buf.clear();
3307   buf.resize(len);
3308   EXPECT_EQ(4370u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
3309   EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
3310             GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
3311 
3312   UnloadPage(page);
3313 }
3314 
TEST_F(FPDFEditEmbedderTest,GetImageMatrix)3315 TEST_F(FPDFEditEmbedderTest, GetImageMatrix) {
3316   ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
3317   FPDF_PAGE page = LoadPage(0);
3318   ASSERT_TRUE(page);
3319   ASSERT_EQ(39, FPDFPage_CountObjects(page));
3320 
3321   FPDF_PAGEOBJECT obj;
3322   double a;
3323   double b;
3324   double c;
3325   double d;
3326   double e;
3327   double f;
3328 
3329   obj = FPDFPage_GetObject(page, 33);
3330   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3331   EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
3332   EXPECT_DOUBLE_EQ(53.0, a);
3333   EXPECT_DOUBLE_EQ(0.0, b);
3334   EXPECT_DOUBLE_EQ(0.0, c);
3335   EXPECT_DOUBLE_EQ(43.0, d);
3336   EXPECT_DOUBLE_EQ(72.0, e);
3337   EXPECT_DOUBLE_EQ(646.510009765625, f);
3338 
3339   obj = FPDFPage_GetObject(page, 34);
3340   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3341   EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
3342   EXPECT_DOUBLE_EQ(70.0, a);
3343   EXPECT_DOUBLE_EQ(0.0, b);
3344   EXPECT_DOUBLE_EQ(0.0, c);
3345   EXPECT_DOUBLE_EQ(51.0, d);
3346   EXPECT_DOUBLE_EQ(216.0, e);
3347   EXPECT_DOUBLE_EQ(646.510009765625, f);
3348 
3349   obj = FPDFPage_GetObject(page, 35);
3350   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3351   EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
3352   EXPECT_DOUBLE_EQ(69.0, a);
3353   EXPECT_DOUBLE_EQ(0.0, b);
3354   EXPECT_DOUBLE_EQ(0.0, c);
3355   EXPECT_DOUBLE_EQ(51.0, d);
3356   EXPECT_DOUBLE_EQ(360.0, e);
3357   EXPECT_DOUBLE_EQ(646.510009765625, f);
3358 
3359   obj = FPDFPage_GetObject(page, 36);
3360   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3361   EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
3362   EXPECT_DOUBLE_EQ(59.0, a);
3363   EXPECT_DOUBLE_EQ(0.0, b);
3364   EXPECT_DOUBLE_EQ(0.0, c);
3365   EXPECT_DOUBLE_EQ(45.0, d);
3366   EXPECT_DOUBLE_EQ(72.0, e);
3367   EXPECT_DOUBLE_EQ(553.510009765625, f);
3368 
3369   obj = FPDFPage_GetObject(page, 37);
3370   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3371   EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
3372   EXPECT_DOUBLE_EQ(55.94000244140625, a);
3373   EXPECT_DOUBLE_EQ(0.0, b);
3374   EXPECT_DOUBLE_EQ(0.0, c);
3375   EXPECT_DOUBLE_EQ(46.950000762939453, d);
3376   EXPECT_DOUBLE_EQ(216.0, e);
3377   EXPECT_DOUBLE_EQ(552.510009765625, f);
3378 
3379   obj = FPDFPage_GetObject(page, 38);
3380   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3381   EXPECT_TRUE(FPDFImageObj_GetMatrix(obj, &a, &b, &c, &d, &e, &f));
3382   EXPECT_DOUBLE_EQ(70.528999328613281, a);
3383   EXPECT_DOUBLE_EQ(0.0, b);
3384   EXPECT_DOUBLE_EQ(0.0, c);
3385   EXPECT_DOUBLE_EQ(43.149997711181641, d);
3386   EXPECT_DOUBLE_EQ(360.0, e);
3387   EXPECT_DOUBLE_EQ(553.3599853515625, f);
3388 
3389   UnloadPage(page);
3390 }
3391 
TEST_F(FPDFEditEmbedderTest,DestroyPageObject)3392 TEST_F(FPDFEditEmbedderTest, DestroyPageObject) {
3393   FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
3394   ASSERT_TRUE(rect);
3395 
3396   // There should be no memory leaks with a call to FPDFPageObj_Destroy().
3397   FPDFPageObj_Destroy(rect);
3398 }
3399 
TEST_F(FPDFEditEmbedderTest,GetImageFilters)3400 TEST_F(FPDFEditEmbedderTest, GetImageFilters) {
3401   EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
3402   FPDF_PAGE page = LoadPage(0);
3403   ASSERT_TRUE(page);
3404 
3405   // Verify that retrieving the filter of a non-image object would fail.
3406   FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 32);
3407   ASSERT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3408   ASSERT_EQ(0, FPDFImageObj_GetImageFilterCount(obj));
3409   EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0));
3410 
3411   // Verify the returned filter string for an image object with a single filter.
3412   obj = FPDFPage_GetObject(page, 33);
3413   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3414   ASSERT_EQ(1, FPDFImageObj_GetImageFilterCount(obj));
3415   unsigned long len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
3416   std::vector<char> buf(len);
3417   static constexpr char kFlateDecode[] = "FlateDecode";
3418   EXPECT_EQ(sizeof(kFlateDecode),
3419             FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
3420   EXPECT_STREQ(kFlateDecode, buf.data());
3421   EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0));
3422 
3423   // Verify all the filters for an image object with a list of filters.
3424   obj = FPDFPage_GetObject(page, 38);
3425   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3426   ASSERT_EQ(2, FPDFImageObj_GetImageFilterCount(obj));
3427   len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
3428   buf.clear();
3429   buf.resize(len);
3430   static constexpr char kASCIIHexDecode[] = "ASCIIHexDecode";
3431   EXPECT_EQ(sizeof(kASCIIHexDecode),
3432             FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
3433   EXPECT_STREQ(kASCIIHexDecode, buf.data());
3434 
3435   len = FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0);
3436   buf.clear();
3437   buf.resize(len);
3438   static constexpr char kDCTDecode[] = "DCTDecode";
3439   EXPECT_EQ(sizeof(kDCTDecode),
3440             FPDFImageObj_GetImageFilter(obj, 1, buf.data(), len));
3441   EXPECT_STREQ(kDCTDecode, buf.data());
3442 
3443   UnloadPage(page);
3444 }
3445 
TEST_F(FPDFEditEmbedderTest,GetImageMetadata)3446 TEST_F(FPDFEditEmbedderTest, GetImageMetadata) {
3447   ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
3448   FPDF_PAGE page = LoadPage(0);
3449   ASSERT_TRUE(page);
3450 
3451   // Check that getting the metadata of a null object would fail.
3452   FPDF_IMAGEOBJ_METADATA metadata;
3453   EXPECT_FALSE(FPDFImageObj_GetImageMetadata(nullptr, page, &metadata));
3454 
3455   // Check that receiving the metadata with a null metadata object would fail.
3456   FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 35);
3457   EXPECT_FALSE(FPDFImageObj_GetImageMetadata(obj, page, nullptr));
3458 
3459   // Check that when retrieving an image object's metadata without passing in
3460   // |page|, all values are correct, with the last two being default values.
3461   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3462   ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, nullptr, &metadata));
3463   EXPECT_EQ(7, metadata.marked_content_id);
3464   EXPECT_EQ(92u, metadata.width);
3465   EXPECT_EQ(68u, metadata.height);
3466   EXPECT_FLOAT_EQ(96.0f, metadata.horizontal_dpi);
3467   EXPECT_FLOAT_EQ(96.0f, metadata.vertical_dpi);
3468   EXPECT_EQ(0u, metadata.bits_per_pixel);
3469   EXPECT_EQ(FPDF_COLORSPACE_UNKNOWN, metadata.colorspace);
3470 
3471   // Verify the metadata of a bitmap image with indexed colorspace.
3472   ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
3473   EXPECT_EQ(7, metadata.marked_content_id);
3474   EXPECT_EQ(92u, metadata.width);
3475   EXPECT_EQ(68u, metadata.height);
3476   EXPECT_FLOAT_EQ(96.0f, metadata.horizontal_dpi);
3477   EXPECT_FLOAT_EQ(96.0f, metadata.vertical_dpi);
3478   EXPECT_EQ(1u, metadata.bits_per_pixel);
3479   EXPECT_EQ(FPDF_COLORSPACE_INDEXED, metadata.colorspace);
3480 
3481   // Verify the metadata of an image with RGB colorspace.
3482   obj = FPDFPage_GetObject(page, 37);
3483   ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
3484   ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
3485   EXPECT_EQ(9, metadata.marked_content_id);
3486   EXPECT_EQ(126u, metadata.width);
3487   EXPECT_EQ(106u, metadata.height);
3488   EXPECT_FLOAT_EQ(162.173752f, metadata.horizontal_dpi);
3489   EXPECT_FLOAT_EQ(162.555878f, metadata.vertical_dpi);
3490   EXPECT_EQ(24u, metadata.bits_per_pixel);
3491   EXPECT_EQ(FPDF_COLORSPACE_DEVICERGB, metadata.colorspace);
3492 
3493   UnloadPage(page);
3494 }
3495