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