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