1 // Copyright 2016 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6 #include <string>
7 #include <utility>
8 #include <vector>
9
10 #include "core/fpdfapi/font/cpdf_font.h"
11 #include "core/fpdfapi/page/cpdf_page.h"
12 #include "core/fpdfapi/parser/cpdf_array.h"
13 #include "core/fpdfapi/parser/cpdf_dictionary.h"
14 #include "core/fpdfapi/parser/cpdf_number.h"
15 #include "core/fpdfapi/parser/cpdf_stream.h"
16 #include "core/fxcrt/fx_system.h"
17 #include "fpdfsdk/fsdk_define.h"
18 #include "public/cpp/fpdf_deleters.h"
19 #include "public/fpdf_annot.h"
20 #include "public/fpdf_edit.h"
21 #include "public/fpdfview.h"
22 #include "testing/embedder_test.h"
23 #include "testing/gmock/include/gmock/gmock-matchers.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "testing/test_support.h"
26
27 class FPDFEditEmbeddertest : public EmbedderTest {
28 protected:
CreateNewDocument()29 FPDF_DOCUMENT CreateNewDocument() {
30 document_ = FPDF_CreateNewDocument();
31 cpdf_doc_ = CPDFDocumentFromFPDFDocument(document_);
32 return document_;
33 }
34
CheckFontDescriptor(CPDF_Dictionary * font_dict,int font_type,bool bold,bool italic,uint32_t size,const uint8_t * data)35 void CheckFontDescriptor(CPDF_Dictionary* font_dict,
36 int font_type,
37 bool bold,
38 bool italic,
39 uint32_t size,
40 const uint8_t* data) {
41 CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor");
42 ASSERT_TRUE(font_desc);
43 EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type"));
44 EXPECT_EQ(font_dict->GetStringFor("BaseFont"),
45 font_desc->GetStringFor("FontName"));
46
47 // Check that the font descriptor has the required keys according to spec
48 // 1.7 Table 5.19
49 ASSERT_TRUE(font_desc->KeyExist("Flags"));
50
51 int font_flags = font_desc->GetIntegerFor("Flags");
52 EXPECT_EQ(bold, FontStyleIsBold(font_flags));
53 EXPECT_EQ(italic, FontStyleIsItalic(font_flags));
54 EXPECT_TRUE(FontStyleIsNonSymbolic(font_flags));
55 ASSERT_TRUE(font_desc->KeyExist("FontBBox"));
56
57 CPDF_Array* fontBBox = font_desc->GetArrayFor("FontBBox");
58 ASSERT_TRUE(fontBBox);
59 EXPECT_EQ(4U, fontBBox->GetCount());
60 // Check that the coordinates are in the preferred order according to spec
61 // 1.7 Section 3.8.4
62 EXPECT_TRUE(fontBBox->GetIntegerAt(0) < fontBBox->GetIntegerAt(2));
63 EXPECT_TRUE(fontBBox->GetIntegerAt(1) < fontBBox->GetIntegerAt(3));
64
65 EXPECT_TRUE(font_desc->KeyExist("ItalicAngle"));
66 EXPECT_TRUE(font_desc->KeyExist("Ascent"));
67 EXPECT_TRUE(font_desc->KeyExist("Descent"));
68 EXPECT_TRUE(font_desc->KeyExist("CapHeight"));
69 EXPECT_TRUE(font_desc->KeyExist("StemV"));
70 ByteString present("FontFile");
71 ByteString absent("FontFile2");
72 if (font_type == FPDF_FONT_TRUETYPE)
73 std::swap(present, absent);
74 EXPECT_TRUE(font_desc->KeyExist(present));
75 EXPECT_FALSE(font_desc->KeyExist(absent));
76
77 // Check that the font stream is the one that was provided
78 CPDF_Stream* font_stream = font_desc->GetStreamFor(present);
79 ASSERT_EQ(size, font_stream->GetRawSize());
80 if (font_type == FPDF_FONT_TRUETYPE) {
81 ASSERT_EQ(static_cast<int>(size),
82 font_stream->GetDict()->GetIntegerFor("Length1"));
83 }
84 uint8_t* stream_data = font_stream->GetRawData();
85 for (size_t j = 0; j < size; j++)
86 EXPECT_EQ(data[j], stream_data[j]) << " at byte " << j;
87 }
88
CheckCompositeFontWidths(CPDF_Array * widths_array,CPDF_Font * typed_font)89 void CheckCompositeFontWidths(CPDF_Array* widths_array,
90 CPDF_Font* typed_font) {
91 // Check that W array is in a format that conforms to PDF spec 1.7 section
92 // "Glyph Metrics in CIDFonts" (these checks are not
93 // implementation-specific).
94 EXPECT_GT(widths_array->GetCount(), 1U);
95 int num_cids_checked = 0;
96 int cur_cid = 0;
97 for (size_t idx = 0; idx < widths_array->GetCount(); idx++) {
98 int cid = widths_array->GetNumberAt(idx);
99 EXPECT_GE(cid, cur_cid);
100 ASSERT_FALSE(++idx == widths_array->GetCount());
101 CPDF_Object* next = widths_array->GetObjectAt(idx);
102 if (next->IsArray()) {
103 // We are in the c [w1 w2 ...] case
104 CPDF_Array* arr = next->AsArray();
105 int cnt = static_cast<int>(arr->GetCount());
106 size_t inner_idx = 0;
107 for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) {
108 int width = arr->GetNumberAt(inner_idx++);
109 EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid "
110 << cur_cid;
111 }
112 num_cids_checked += cnt;
113 continue;
114 }
115 // Otherwise, are in the c_first c_last w case.
116 ASSERT_TRUE(next->IsNumber());
117 int last_cid = next->AsNumber()->GetInteger();
118 ASSERT_FALSE(++idx == widths_array->GetCount());
119 int width = widths_array->GetNumberAt(idx);
120 for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) {
121 EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid "
122 << cur_cid;
123 }
124 num_cids_checked += last_cid - cid + 1;
125 }
126 // Make sure we have a good amount of cids described
127 EXPECT_GT(num_cids_checked, 900);
128 }
cpdf_doc()129 CPDF_Document* cpdf_doc() { return cpdf_doc_; }
130
131 private:
132 CPDF_Document* cpdf_doc_;
133 };
134
135 namespace {
136
137 const char kExpectedPDF[] =
138 "%PDF-1.7\r\n"
139 "%\xA1\xB3\xC5\xD7\r\n"
140 "1 0 obj\r\n"
141 "<</Pages 2 0 R /Type/Catalog>>\r\n"
142 "endobj\r\n"
143 "2 0 obj\r\n"
144 "<</Count 1/Kids\\[ 4 0 R \\]/Type/Pages>>\r\n"
145 "endobj\r\n"
146 "3 0 obj\r\n"
147 "<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
148 "endobj\r\n"
149 "4 0 obj\r\n"
150 "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R "
151 "/Resources<</ExtGState<</FXE1 5 0 R >>>>"
152 "/Rotate 0/Type/Page"
153 ">>\r\n"
154 "endobj\r\n"
155 "5 0 obj\r\n"
156 "<</BM/Normal/CA 1/ca 1>>\r\n"
157 "endobj\r\n"
158 "xref\r\n"
159 "0 6\r\n"
160 "0000000000 65535 f\r\n"
161 "0000000017 00000 n\r\n"
162 "0000000066 00000 n\r\n"
163 "0000000122 00000 n\r\n"
164 "0000000192 00000 n\r\n"
165 "0000000311 00000 n\r\n"
166 "trailer\r\n"
167 "<<\r\n"
168 "/Root 1 0 R\r\n"
169 "/Info 3 0 R\r\n"
170 "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
171 "startxref\r\n"
172 "354\r\n"
173 "%%EOF\r\n";
174
175 } // namespace
176
TEST_F(FPDFEditEmbeddertest,EmptyCreation)177 TEST_F(FPDFEditEmbeddertest, EmptyCreation) {
178 EXPECT_TRUE(CreateEmptyDocument());
179 FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
180 EXPECT_NE(nullptr, page);
181 // The FPDFPage_GenerateContent call should do nothing.
182 EXPECT_TRUE(FPDFPage_GenerateContent(page));
183 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
184
185 EXPECT_THAT(GetString(), testing::MatchesRegex(std::string(
186 kExpectedPDF, sizeof(kExpectedPDF))));
187 FPDF_ClosePage(page);
188 }
189
190 // Regression test for https://crbug.com/667012
TEST_F(FPDFEditEmbeddertest,RasterizePDF)191 TEST_F(FPDFEditEmbeddertest, RasterizePDF) {
192 const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
193
194 // Get the bitmap for the original document/
195 FPDF_BITMAP orig_bitmap;
196 {
197 EXPECT_TRUE(OpenDocument("black.pdf"));
198 FPDF_PAGE orig_page = LoadPage(0);
199 EXPECT_NE(nullptr, orig_page);
200 orig_bitmap = RenderPage(orig_page);
201 CompareBitmap(orig_bitmap, 612, 792, kAllBlackMd5sum);
202 UnloadPage(orig_page);
203 }
204
205 // Create a new document from |orig_bitmap| and save it.
206 {
207 FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
208 FPDF_PAGE temp_page = FPDFPage_New(temp_doc, 0, 612, 792);
209
210 // Add the bitmap to an image object and add the image object to the output
211 // page.
212 FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);
213 EXPECT_TRUE(FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap));
214 EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
215 FPDFPage_InsertObject(temp_page, temp_img);
216 EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
217 EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
218 FPDF_ClosePage(temp_page);
219 FPDF_CloseDocument(temp_doc);
220 }
221 FPDFBitmap_Destroy(orig_bitmap);
222
223 // Get the generated content. Make sure it is at least as big as the original
224 // PDF.
225 EXPECT_GT(GetString().size(), 923U);
226 VerifySavedDocument(612, 792, kAllBlackMd5sum);
227 }
228
TEST_F(FPDFEditEmbeddertest,AddPaths)229 TEST_F(FPDFEditEmbeddertest, AddPaths) {
230 // Start with a blank page
231 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
232
233 // We will first add a red rectangle
234 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
235 ASSERT_NE(nullptr, red_rect);
236 // Expect false when trying to set colors out of range
237 EXPECT_FALSE(FPDFPath_SetStrokeColor(red_rect, 100, 100, 100, 300));
238 EXPECT_FALSE(FPDFPath_SetFillColor(red_rect, 200, 256, 200, 0));
239
240 // Fill rectangle with red and insert to the page
241 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
242 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
243 FPDFPage_InsertObject(page, red_rect);
244 FPDF_BITMAP page_bitmap = RenderPage(page);
245 CompareBitmap(page_bitmap, 612, 792, "66d02eaa6181e2c069ce2ea99beda497");
246 FPDFBitmap_Destroy(page_bitmap);
247
248 // Now add to that a green rectangle with some medium alpha
249 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
250 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 128));
251
252 // Make sure the type of the rectangle is a path.
253 EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(green_rect));
254
255 // Make sure we get back the same color we set previously.
256 unsigned int R;
257 unsigned int G;
258 unsigned int B;
259 unsigned int A;
260 EXPECT_TRUE(FPDFPath_GetFillColor(green_rect, &R, &G, &B, &A));
261 EXPECT_EQ(0U, R);
262 EXPECT_EQ(255U, G);
263 EXPECT_EQ(0U, B);
264 EXPECT_EQ(128U, A);
265
266 // Make sure the path has 5 points (1 FXPT_TYPE::MoveTo and 4
267 // FXPT_TYPE::LineTo).
268 ASSERT_EQ(5, FPDFPath_CountSegments(green_rect));
269 // Verify actual coordinates.
270 FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(green_rect, 0);
271 float x;
272 float y;
273 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
274 EXPECT_EQ(100, x);
275 EXPECT_EQ(100, y);
276 EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
277 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
278 segment = FPDFPath_GetPathSegment(green_rect, 1);
279 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
280 EXPECT_EQ(100, x);
281 EXPECT_EQ(140, y);
282 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
283 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
284 segment = FPDFPath_GetPathSegment(green_rect, 2);
285 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
286 EXPECT_EQ(140, x);
287 EXPECT_EQ(140, y);
288 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
289 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
290 segment = FPDFPath_GetPathSegment(green_rect, 3);
291 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
292 EXPECT_EQ(140, x);
293 EXPECT_EQ(100, y);
294 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
295 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
296 segment = FPDFPath_GetPathSegment(green_rect, 4);
297 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
298 EXPECT_EQ(100, x);
299 EXPECT_EQ(100, y);
300 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
301 EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
302
303 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
304 FPDFPage_InsertObject(page, green_rect);
305 page_bitmap = RenderPage(page);
306 CompareBitmap(page_bitmap, 612, 792, "7b0b87604594e773add528fae567a558");
307 FPDFBitmap_Destroy(page_bitmap);
308
309 // Add a black triangle.
310 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
311 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 200));
312 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
313 EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
314 EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
315 EXPECT_TRUE(FPDFPath_Close(black_path));
316
317 // Make sure the path has 3 points (1 FXPT_TYPE::MoveTo and 2
318 // FXPT_TYPE::LineTo).
319 ASSERT_EQ(3, FPDFPath_CountSegments(black_path));
320 // Verify actual coordinates.
321 segment = FPDFPath_GetPathSegment(black_path, 0);
322 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
323 EXPECT_EQ(400, x);
324 EXPECT_EQ(100, y);
325 EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
326 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
327 segment = FPDFPath_GetPathSegment(black_path, 1);
328 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
329 EXPECT_EQ(400, x);
330 EXPECT_EQ(200, y);
331 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
332 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
333 segment = FPDFPath_GetPathSegment(black_path, 2);
334 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
335 EXPECT_EQ(300, x);
336 EXPECT_EQ(100, y);
337 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
338 EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
339 // Make sure out of bounds index access fails properly.
340 EXPECT_EQ(nullptr, FPDFPath_GetPathSegment(black_path, 3));
341
342 FPDFPage_InsertObject(page, black_path);
343 page_bitmap = RenderPage(page);
344 CompareBitmap(page_bitmap, 612, 792, "eadc8020a14dfcf091da2688733d8806");
345 FPDFBitmap_Destroy(page_bitmap);
346
347 // Now add a more complex blue path.
348 FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
349 EXPECT_TRUE(FPDFPath_SetFillColor(blue_path, 0, 0, 255, 255));
350 EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
351 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
352 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
353 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
354 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
355 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
356 EXPECT_TRUE(FPDFPath_Close(blue_path));
357 FPDFPage_InsertObject(page, blue_path);
358 page_bitmap = RenderPage(page);
359 const char last_md5[] = "9823e1a21bd9b72b6a442ba4f12af946";
360 CompareBitmap(page_bitmap, 612, 792, last_md5);
361 FPDFBitmap_Destroy(page_bitmap);
362
363 // Now save the result, closing the page and document
364 EXPECT_TRUE(FPDFPage_GenerateContent(page));
365 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
366 FPDF_ClosePage(page);
367
368 // Render the saved result
369 VerifySavedDocument(612, 792, last_md5);
370 }
371
TEST_F(FPDFEditEmbeddertest,PathsPoints)372 TEST_F(FPDFEditEmbeddertest, PathsPoints) {
373 CreateNewDocument();
374 FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document_);
375 // This should fail gracefully, even if img is not a path.
376 ASSERT_EQ(-1, FPDFPath_CountSegments(img));
377
378 // This should fail gracefully, even if path is NULL.
379 ASSERT_EQ(-1, FPDFPath_CountSegments(nullptr));
380
381 // FPDFPath_GetPathSegment() with a non-path.
382 ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(img, 0));
383 // FPDFPath_GetPathSegment() with a NULL path.
384 ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(nullptr, 0));
385 float x;
386 float y;
387 // FPDFPathSegment_GetPoint() with a NULL segment.
388 EXPECT_FALSE(FPDFPathSegment_GetPoint(nullptr, &x, &y));
389
390 // FPDFPathSegment_GetType() with a NULL segment.
391 ASSERT_EQ(FPDF_SEGMENT_UNKNOWN, FPDFPathSegment_GetType(nullptr));
392
393 // FPDFPathSegment_GetClose() with a NULL segment.
394 EXPECT_FALSE(FPDFPathSegment_GetClose(nullptr));
395
396 FPDFPageObj_Destroy(img);
397 }
398
TEST_F(FPDFEditEmbeddertest,PathOnTopOfText)399 TEST_F(FPDFEditEmbeddertest, PathOnTopOfText) {
400 // Load document with some text
401 EXPECT_TRUE(OpenDocument("hello_world.pdf"));
402 FPDF_PAGE page = LoadPage(0);
403 EXPECT_NE(nullptr, page);
404
405 // Add an opaque rectangle on top of some of the text.
406 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
407 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
408 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
409 FPDFPage_InsertObject(page, red_rect);
410
411 // Add a transparent triangle on top of other part of the text.
412 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
413 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 100));
414 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
415 EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
416 EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
417 EXPECT_TRUE(FPDFPath_Close(black_path));
418 FPDFPage_InsertObject(page, black_path);
419
420 // Render and check the result. Text is slightly different on Mac.
421 FPDF_BITMAP bitmap = RenderPage(page);
422 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
423 const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
424 #else
425 const char md5[] = "aa71b09b93b55f467f1290e5111babee";
426 #endif
427 CompareBitmap(bitmap, 200, 200, md5);
428 FPDFBitmap_Destroy(bitmap);
429 UnloadPage(page);
430 }
431
TEST_F(FPDFEditEmbeddertest,EditOverExistingContent)432 TEST_F(FPDFEditEmbeddertest, EditOverExistingContent) {
433 // Load document with existing content
434 EXPECT_TRUE(OpenDocument("bug_717.pdf"));
435 FPDF_PAGE page = LoadPage(0);
436 EXPECT_NE(nullptr, page);
437
438 // Add a transparent rectangle on top of the existing content
439 FPDF_PAGEOBJECT red_rect2 = FPDFPageObj_CreateNewRect(90, 700, 25, 50);
440 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect2, 255, 0, 0, 100));
441 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect2, FPDF_FILLMODE_ALTERNATE, 0));
442 FPDFPage_InsertObject(page, red_rect2);
443
444 // Add an opaque rectangle on top of the existing content
445 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(115, 700, 25, 50);
446 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
447 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
448 FPDFPage_InsertObject(page, red_rect);
449
450 FPDF_BITMAP bitmap = RenderPage(page);
451 CompareBitmap(bitmap, 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
452 FPDFBitmap_Destroy(bitmap);
453 EXPECT_TRUE(FPDFPage_GenerateContent(page));
454
455 // Now save the result, closing the page and document
456 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
457 UnloadPage(page);
458
459 OpenSavedDocument();
460 page = LoadSavedPage(0);
461 VerifySavedRendering(page, 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
462
463 ClearString();
464 // Add another opaque rectangle on top of the existing content
465 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(150, 700, 25, 50);
466 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 255));
467 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_ALTERNATE, 0));
468 FPDFPage_InsertObject(page, green_rect);
469
470 // Add another transparent rectangle on top of existing content
471 FPDF_PAGEOBJECT green_rect2 = FPDFPageObj_CreateNewRect(175, 700, 25, 50);
472 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect2, 0, 255, 0, 100));
473 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect2, FPDF_FILLMODE_ALTERNATE, 0));
474 FPDFPage_InsertObject(page, green_rect2);
475 FPDF_BITMAP new_bitmap = RenderPageWithFlags(page, m_SavedForm, 0);
476 const char last_md5[] = "4b5b00f824620f8c9b8801ebb98e1cdd";
477 CompareBitmap(new_bitmap, 612, 792, last_md5);
478 FPDFBitmap_Destroy(new_bitmap);
479 EXPECT_TRUE(FPDFPage_GenerateContent(page));
480
481 // Now save the result, closing the page and document
482 EXPECT_TRUE(FPDF_SaveAsCopy(m_SavedDocument, this, 0));
483
484 CloseSavedPage(page);
485 CloseSavedDocument();
486
487 // Render the saved result
488 VerifySavedDocument(612, 792, last_md5);
489 }
490
TEST_F(FPDFEditEmbeddertest,AddStrokedPaths)491 TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) {
492 // Start with a blank page
493 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
494
495 // Add a large stroked rectangle (fill color should not affect it).
496 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
497 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 255));
498 EXPECT_TRUE(FPDFPath_SetStrokeColor(rect, 0, 255, 0, 255));
499 EXPECT_TRUE(FPDFPath_SetStrokeWidth(rect, 15.0f));
500 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
501 FPDFPage_InsertObject(page, rect);
502 FPDF_BITMAP page_bitmap = RenderPage(page);
503 CompareBitmap(page_bitmap, 612, 792, "64bd31f862a89e0a9e505a5af6efd506");
504 FPDFBitmap_Destroy(page_bitmap);
505
506 // Add crossed-checkmark
507 FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
508 EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
509 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
510 EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
511 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
512 EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 128, 128, 128, 180));
513 EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
514 EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
515 FPDFPage_InsertObject(page, check);
516 page_bitmap = RenderPage(page);
517 CompareBitmap(page_bitmap, 612, 792, "4b6f3b9d25c4e194821217d5016c3724");
518 FPDFBitmap_Destroy(page_bitmap);
519
520 // Add stroked and filled oval-ish path.
521 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
522 EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
523 EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
524 EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
525 EXPECT_TRUE(FPDFPath_Close(path));
526 EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 128, 128, 100));
527 EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 128, 200, 128, 150));
528 EXPECT_TRUE(FPDFPath_SetStrokeWidth(path, 10.5f));
529 EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
530 FPDFPage_InsertObject(page, path);
531 page_bitmap = RenderPage(page);
532 CompareBitmap(page_bitmap, 612, 792, "ff3e6a22326754944cc6e56609acd73b");
533 FPDFBitmap_Destroy(page_bitmap);
534 FPDF_ClosePage(page);
535 }
536
TEST_F(FPDFEditEmbeddertest,AddStandardFontText)537 TEST_F(FPDFEditEmbeddertest, AddStandardFontText) {
538 // Start with a blank page
539 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
540
541 // Add some text to the page
542 FPDF_PAGEOBJECT text_object1 =
543 FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
544 EXPECT_TRUE(text_object1);
545 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text1 =
546 GetFPDFWideString(L"I'm at the bottom of the page");
547 EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get()));
548 FPDFPageObj_Transform(text_object1, 1, 0, 0, 1, 20, 20);
549 FPDFPage_InsertObject(page, text_object1);
550 FPDF_BITMAP page_bitmap = RenderPage(page);
551 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
552 const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
553 #else
554 const char md5[] = "eacaa24573b8ce997b3882595f096f00";
555 #endif
556 CompareBitmap(page_bitmap, 612, 792, md5);
557 FPDFBitmap_Destroy(page_bitmap);
558
559 // Try another font
560 FPDF_PAGEOBJECT text_object2 =
561 FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
562 EXPECT_TRUE(text_object2);
563 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
564 GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold.");
565 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
566 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600);
567 FPDFPage_InsertObject(page, text_object2);
568 page_bitmap = RenderPage(page);
569 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
570 const char md5_2[] = "a5c4ace4c6f27644094813fe1441a21c";
571 #elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
572 const char md5_2[] = "2587eac9a787e97a37636d54d11bd28d";
573 #else
574 const char md5_2[] = "76fcc7d08aa15445efd2e2ceb7c6cc3b";
575 #endif
576 CompareBitmap(page_bitmap, 612, 792, md5_2);
577 FPDFBitmap_Destroy(page_bitmap);
578
579 // And some randomly transformed text
580 FPDF_PAGEOBJECT text_object3 =
581 FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
582 EXPECT_TRUE(text_object3);
583 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text3 =
584 GetFPDFWideString(L"Can you read me? <:)>");
585 EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get()));
586 FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200);
587 FPDFPage_InsertObject(page, text_object3);
588 page_bitmap = RenderPage(page);
589 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
590 const char md5_3[] = "40b3ef04f915ff4c4208948001763544";
591 #elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
592 const char md5_3[] = "7cb61ec112cf400b489360d443ffc9d2";
593 #else
594 const char md5_3[] = "b8a21668f1dab625af7c072e07fcefc4";
595 #endif
596 CompareBitmap(page_bitmap, 612, 792, md5_3);
597 FPDFBitmap_Destroy(page_bitmap);
598
599 // TODO(npm): Why are there issues with text rotated by 90 degrees?
600 // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
601 FPDF_ClosePage(page);
602 }
603
TEST_F(FPDFEditEmbeddertest,GraphicsData)604 TEST_F(FPDFEditEmbeddertest, GraphicsData) {
605 // New page
606 std::unique_ptr<void, FPDFPageDeleter> page(
607 FPDFPage_New(CreateNewDocument(), 0, 612, 792));
608
609 // Create a rect with nontrivial graphics
610 FPDF_PAGEOBJECT rect1 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
611 FPDFPageObj_SetBlendMode(rect1, "Color");
612 FPDFPage_InsertObject(page.get(), rect1);
613 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
614
615 // Check that the ExtGState was created
616 CPDF_Page* the_page = CPDFPageFromFPDFPage(page.get());
617 CPDF_Dictionary* graphics_dict =
618 the_page->m_pResources->GetDictFor("ExtGState");
619 ASSERT_TRUE(graphics_dict);
620 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
621
622 // Add a text object causing no change to the graphics dictionary
623 FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
624 // Only alpha, the last component, matters for the graphics dictionary. And
625 // the default value is 255.
626 EXPECT_TRUE(FPDFText_SetFillColor(text1, 100, 100, 100, 255));
627 FPDFPage_InsertObject(page.get(), text1);
628 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
629 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
630
631 // Add a text object increasing the size of the graphics dictionary
632 FPDF_PAGEOBJECT text2 =
633 FPDFPageObj_NewTextObj(document(), "Times-Roman", 12.0f);
634 FPDFPage_InsertObject(page.get(), text2);
635 FPDFPageObj_SetBlendMode(text2, "Darken");
636 EXPECT_TRUE(FPDFText_SetFillColor(text2, 0, 0, 255, 150));
637 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
638 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
639
640 // Add a path that should reuse graphics
641 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
642 FPDFPageObj_SetBlendMode(path, "Darken");
643 EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 200, 100, 150));
644 FPDFPage_InsertObject(page.get(), path);
645 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
646 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
647
648 // Add a rect increasing the size of the graphics dictionary
649 FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
650 FPDFPageObj_SetBlendMode(rect2, "Darken");
651 EXPECT_TRUE(FPDFPath_SetFillColor(rect2, 0, 0, 255, 150));
652 EXPECT_TRUE(FPDFPath_SetStrokeColor(rect2, 0, 0, 0, 200));
653 FPDFPage_InsertObject(page.get(), rect2);
654 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
655 EXPECT_EQ(4, static_cast<int>(graphics_dict->GetCount()));
656 }
657
TEST_F(FPDFEditEmbeddertest,DoubleGenerating)658 TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
659 // Start with a blank page
660 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
661
662 // Add a red rectangle with some non-default alpha
663 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
664 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 128));
665 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
666 FPDFPage_InsertObject(page, rect);
667 EXPECT_TRUE(FPDFPage_GenerateContent(page));
668
669 // Check the ExtGState
670 CPDF_Page* the_page = CPDFPageFromFPDFPage(page);
671 CPDF_Dictionary* graphics_dict =
672 the_page->m_pResources->GetDictFor("ExtGState");
673 ASSERT_TRUE(graphics_dict);
674 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
675
676 // Check the bitmap
677 FPDF_BITMAP page_bitmap = RenderPage(page);
678 CompareBitmap(page_bitmap, 612, 792, "5384da3406d62360ffb5cac4476fff1c");
679 FPDFBitmap_Destroy(page_bitmap);
680
681 // Never mind, my new favorite color is blue, increase alpha
682 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 0, 0, 255, 180));
683 EXPECT_TRUE(FPDFPage_GenerateContent(page));
684 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
685
686 // Check that bitmap displays changed content
687 page_bitmap = RenderPage(page);
688 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
689 FPDFBitmap_Destroy(page_bitmap);
690
691 // And now generate, without changes
692 EXPECT_TRUE(FPDFPage_GenerateContent(page));
693 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
694 page_bitmap = RenderPage(page);
695 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
696 FPDFBitmap_Destroy(page_bitmap);
697
698 // Add some text to the page
699 FPDF_PAGEOBJECT text_object =
700 FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
701 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
702 GetFPDFWideString(L"Something something #text# something");
703 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
704 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300);
705 FPDFPage_InsertObject(page, text_object);
706 EXPECT_TRUE(FPDFPage_GenerateContent(page));
707 CPDF_Dictionary* font_dict = the_page->m_pResources->GetDictFor("Font");
708 ASSERT_TRUE(font_dict);
709 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
710
711 // Generate yet again, check dicts are reasonably sized
712 EXPECT_TRUE(FPDFPage_GenerateContent(page));
713 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
714 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
715 FPDF_ClosePage(page);
716 }
717
TEST_F(FPDFEditEmbeddertest,LoadSimpleType1Font)718 TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) {
719 CreateNewDocument();
720 // TODO(npm): use other fonts after disallowing loading any font as any type
721 const CPDF_Font* stock_font =
722 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
723 const uint8_t* data = stock_font->GetFont()->GetFontData();
724 const uint32_t size = stock_font->GetFont()->GetSize();
725 std::unique_ptr<void, FPDFFontDeleter> font(
726 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false));
727 ASSERT_TRUE(font.get());
728 CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
729 EXPECT_TRUE(typed_font->IsType1Font());
730
731 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
732 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
733 EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
734 EXPECT_EQ("Times New Roman Bold", font_dict->GetStringFor("BaseFont"));
735 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
736 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
737 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
738 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
739
740 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
741 ASSERT_TRUE(widths_array);
742 ASSERT_EQ(224U, widths_array->GetCount());
743 EXPECT_EQ(250, widths_array->GetNumberAt(0));
744 EXPECT_EQ(569, widths_array->GetNumberAt(11));
745 EXPECT_EQ(500, widths_array->GetNumberAt(223));
746 CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, size, data);
747 }
748
TEST_F(FPDFEditEmbeddertest,LoadSimpleTrueTypeFont)749 TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) {
750 CreateNewDocument();
751 const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
752 const uint8_t* data = stock_font->GetFont()->GetFontData();
753 const uint32_t size = stock_font->GetFont()->GetSize();
754 std::unique_ptr<void, FPDFFontDeleter> font(
755 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false));
756 ASSERT_TRUE(font.get());
757 CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
758 EXPECT_TRUE(typed_font->IsTrueTypeFont());
759
760 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
761 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
762 EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
763 EXPECT_EQ("Courier New", font_dict->GetStringFor("BaseFont"));
764 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
765 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
766 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
767 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
768
769 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
770 ASSERT_TRUE(widths_array);
771 ASSERT_EQ(224U, widths_array->GetCount());
772 EXPECT_EQ(600, widths_array->GetNumberAt(33));
773 EXPECT_EQ(600, widths_array->GetNumberAt(74));
774 EXPECT_EQ(600, widths_array->GetNumberAt(223));
775 CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, size, data);
776 }
777
TEST_F(FPDFEditEmbeddertest,LoadCIDType0Font)778 TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) {
779 CreateNewDocument();
780 const CPDF_Font* stock_font =
781 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
782 const uint8_t* data = stock_font->GetFont()->GetFontData();
783 const uint32_t size = stock_font->GetFont()->GetSize();
784 std::unique_ptr<void, FPDFFontDeleter> font(
785 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1));
786 ASSERT_TRUE(font.get());
787 CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
788 EXPECT_TRUE(typed_font->IsCIDFont());
789
790 // Check font dictionary entries
791 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
792 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
793 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
794 EXPECT_EQ("Times New Roman-Identity-H", font_dict->GetStringFor("BaseFont"));
795 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
796 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
797 ASSERT_TRUE(descendant_array);
798 EXPECT_EQ(1U, descendant_array->GetCount());
799
800 // Check the CIDFontDict
801 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
802 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
803 EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
804 EXPECT_EQ("Times New Roman", cidfont_dict->GetStringFor("BaseFont"));
805 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
806 ASSERT_TRUE(cidinfo_dict);
807 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
808 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
809 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
810 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, size, data);
811
812 // Check widths
813 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
814 ASSERT_TRUE(widths_array);
815 EXPECT_GT(widths_array->GetCount(), 1U);
816 CheckCompositeFontWidths(widths_array, typed_font);
817 }
818
TEST_F(FPDFEditEmbeddertest,LoadCIDType2Font)819 TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) {
820 CreateNewDocument();
821 const CPDF_Font* stock_font =
822 CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
823 const uint8_t* data = stock_font->GetFont()->GetFontData();
824 const uint32_t size = stock_font->GetFont()->GetSize();
825
826 std::unique_ptr<void, FPDFFontDeleter> font(
827 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1));
828 ASSERT_TRUE(font.get());
829 CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
830 EXPECT_TRUE(typed_font->IsCIDFont());
831
832 // Check font dictionary entries
833 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
834 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
835 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
836 EXPECT_EQ("Arial Italic", font_dict->GetStringFor("BaseFont"));
837 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
838 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
839 ASSERT_TRUE(descendant_array);
840 EXPECT_EQ(1U, descendant_array->GetCount());
841
842 // Check the CIDFontDict
843 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
844 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
845 EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
846 EXPECT_EQ("Arial Italic", cidfont_dict->GetStringFor("BaseFont"));
847 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
848 ASSERT_TRUE(cidinfo_dict);
849 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
850 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
851 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
852 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, size,
853 data);
854
855 // Check widths
856 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
857 ASSERT_TRUE(widths_array);
858 CheckCompositeFontWidths(widths_array, typed_font);
859 }
860
TEST_F(FPDFEditEmbeddertest,NormalizeNegativeRotation)861 TEST_F(FPDFEditEmbeddertest, NormalizeNegativeRotation) {
862 // Load document with a -90 degree rotation
863 EXPECT_TRUE(OpenDocument("bug_713197.pdf"));
864 FPDF_PAGE page = LoadPage(0);
865 EXPECT_NE(nullptr, page);
866
867 EXPECT_EQ(3, FPDFPage_GetRotation(page));
868 UnloadPage(page);
869 }
870
TEST_F(FPDFEditEmbeddertest,AddTrueTypeFontText)871 TEST_F(FPDFEditEmbeddertest, AddTrueTypeFontText) {
872 // Start with a blank page
873 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
874 {
875 const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
876 const uint8_t* data = stock_font->GetFont()->GetFontData();
877 const uint32_t size = stock_font->GetFont()->GetSize();
878 std::unique_ptr<void, FPDFFontDeleter> font(
879 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 0));
880 ASSERT_TRUE(font.get());
881
882 // Add some text to the page
883 FPDF_PAGEOBJECT text_object =
884 FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
885 EXPECT_TRUE(text_object);
886 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
887 GetFPDFWideString(L"I am testing my loaded font, WEE.");
888 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
889 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
890 FPDFPage_InsertObject(page, text_object);
891 FPDF_BITMAP page_bitmap = RenderPage(page);
892 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
893 const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
894 #else
895 const char md5[] = "70592859010ffbf532a2237b8118bcc4";
896 #endif // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
897 CompareBitmap(page_bitmap, 612, 792, md5);
898 FPDFBitmap_Destroy(page_bitmap);
899
900 // Add some more text, same font
901 FPDF_PAGEOBJECT text_object2 =
902 FPDFPageObj_CreateTextObj(document(), font.get(), 15.0f);
903 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
904 GetFPDFWideString(L"Bigger font size");
905 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
906 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 200, 200);
907 FPDFPage_InsertObject(page, text_object2);
908 }
909 FPDF_BITMAP page_bitmap2 = RenderPage(page);
910 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
911 const char md5_2[] = "8eded4193ff1f0f77b8b600a825e97ea";
912 #else
913 const char md5_2[] = "c1d10cce1761c4a998a16b2562030568";
914 #endif // _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
915 CompareBitmap(page_bitmap2, 612, 792, md5_2);
916 FPDFBitmap_Destroy(page_bitmap2);
917
918 EXPECT_TRUE(FPDFPage_GenerateContent(page));
919 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
920 FPDF_ClosePage(page);
921
922 VerifySavedDocument(612, 792, md5_2);
923 }
924
TEST_F(FPDFEditEmbeddertest,TransformAnnot)925 TEST_F(FPDFEditEmbeddertest, TransformAnnot) {
926 // Open a file with one annotation and load its first page.
927 ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
928 FPDF_PAGE page = FPDF_LoadPage(document(), 0);
929 ASSERT_TRUE(page);
930
931 // Add an underline annotation to the page without specifying its rectangle.
932 FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE);
933 ASSERT_TRUE(annot);
934
935 // FPDFPage_TransformAnnots() should run without errors when modifying
936 // annotation rectangles.
937 FPDFPage_TransformAnnots(page, 1, 2, 3, 4, 5, 6);
938
939 FPDFPage_CloseAnnot(annot);
940 UnloadPage(page);
941 }
942
943 // TODO(npm): Add tests using Japanese fonts in other OS.
944 #if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
TEST_F(FPDFEditEmbeddertest,AddCIDFontText)945 TEST_F(FPDFEditEmbeddertest, AddCIDFontText) {
946 // Start with a blank page
947 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
948 CFX_Font CIDfont;
949 {
950 // First, get the data from the font
951 CIDfont.LoadSubst("IPAGothic", 1, 0, 400, 0, 932, 0);
952 EXPECT_EQ("IPAGothic", CIDfont.GetFaceName());
953 const uint8_t* data = CIDfont.GetFontData();
954 const uint32_t size = CIDfont.GetSize();
955
956 // Load the data into a FPDF_Font.
957 std::unique_ptr<void, FPDFFontDeleter> font(
958 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1));
959 ASSERT_TRUE(font.get());
960
961 // Add some text to the page
962 FPDF_PAGEOBJECT text_object =
963 FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
964 ASSERT_TRUE(text_object);
965 std::wstring wstr = L"ABCDEFGhijklmnop.";
966 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
967 GetFPDFWideString(wstr);
968 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
969 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 200);
970 FPDFPage_InsertObject(page, text_object);
971
972 // And add some Japanese characters
973 FPDF_PAGEOBJECT text_object2 =
974 FPDFPageObj_CreateTextObj(document(), font.get(), 18.0f);
975 ASSERT_TRUE(text_object2);
976 std::wstring wstr2 =
977 L"\u3053\u3093\u306B\u3061\u306f\u4e16\u754C\u3002\u3053\u3053\u306B1"
978 L"\u756A";
979 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
980 GetFPDFWideString(wstr2);
981 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
982 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 500);
983 FPDFPage_InsertObject(page, text_object2);
984 }
985
986 // Check that the text renders properly.
987 FPDF_BITMAP page_bitmap = RenderPage(page);
988 const char md5[] = "c68cd79aa72bf83a7b25271370d46b21";
989 CompareBitmap(page_bitmap, 612, 792, md5);
990 FPDFBitmap_Destroy(page_bitmap);
991
992 // Save the document, close the page.
993 EXPECT_TRUE(FPDFPage_GenerateContent(page));
994 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
995 FPDF_ClosePage(page);
996
997 VerifySavedDocument(612, 792, md5);
998 }
999 #endif // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
1000
TEST_F(FPDFEditEmbeddertest,SaveAndRender)1001 TEST_F(FPDFEditEmbeddertest, SaveAndRender) {
1002 const char md5[] = "3c20472b0552c0c22b88ab1ed8c6202b";
1003 {
1004 EXPECT_TRUE(OpenDocument("bug_779.pdf"));
1005 FPDF_PAGE page = LoadPage(0);
1006 ASSERT_NE(nullptr, page);
1007
1008 // Now add a more complex blue path.
1009 FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20);
1010 EXPECT_TRUE(FPDFPath_SetFillColor(green_path, 0, 255, 0, 200));
1011 // TODO(npm): stroking will cause the MD5s to differ.
1012 EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0));
1013 EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63));
1014 EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90));
1015 EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 133));
1016 EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 33));
1017 EXPECT_TRUE(FPDFPath_BezierTo(green_path, 38, 33, 39, 36, 40, 40));
1018 EXPECT_TRUE(FPDFPath_Close(green_path));
1019 FPDFPage_InsertObject(page, green_path);
1020 FPDF_BITMAP page_bitmap = RenderPage(page);
1021 CompareBitmap(page_bitmap, 612, 792, md5);
1022 FPDFBitmap_Destroy(page_bitmap);
1023
1024 // Now save the result, closing the page and document
1025 EXPECT_TRUE(FPDFPage_GenerateContent(page));
1026 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1027 UnloadPage(page);
1028 }
1029
1030 VerifySavedDocument(612, 792, md5);
1031 }
1032
TEST_F(FPDFEditEmbeddertest,ExtractImageBitmap)1033 TEST_F(FPDFEditEmbeddertest, ExtractImageBitmap) {
1034 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
1035 FPDF_PAGE page = LoadPage(0);
1036 ASSERT_TRUE(page);
1037 ASSERT_EQ(39, FPDFPage_CountObjects(page));
1038
1039 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 32);
1040 EXPECT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1041 EXPECT_FALSE(FPDFImageObj_GetBitmap(obj));
1042
1043 obj = FPDFPage_GetObject(page, 33);
1044 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1045 FPDF_BITMAP bitmap = FPDFImageObj_GetBitmap(obj);
1046 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
1047 CompareBitmap(bitmap, 109, 88, "d65e98d968d196abf13f78aec655ffae");
1048 FPDFBitmap_Destroy(bitmap);
1049
1050 obj = FPDFPage_GetObject(page, 34);
1051 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1052 bitmap = FPDFImageObj_GetBitmap(obj);
1053 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
1054 CompareBitmap(bitmap, 103, 75, "1287711c84dbef767c435d11697661d6");
1055 FPDFBitmap_Destroy(bitmap);
1056
1057 obj = FPDFPage_GetObject(page, 35);
1058 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1059 bitmap = FPDFImageObj_GetBitmap(obj);
1060 EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap));
1061 CompareBitmap(bitmap, 92, 68, "9c6d76cb1e37ef8514f9455d759391f3");
1062 FPDFBitmap_Destroy(bitmap);
1063
1064 obj = FPDFPage_GetObject(page, 36);
1065 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1066 bitmap = FPDFImageObj_GetBitmap(obj);
1067 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
1068 CompareBitmap(bitmap, 79, 60, "15cb6a49a2e354ed0e9f45dd34e3da1a");
1069 FPDFBitmap_Destroy(bitmap);
1070
1071 obj = FPDFPage_GetObject(page, 37);
1072 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1073 bitmap = FPDFImageObj_GetBitmap(obj);
1074 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
1075 CompareBitmap(bitmap, 126, 106, "be5a64ba7890d2657522af6524118534");
1076 FPDFBitmap_Destroy(bitmap);
1077
1078 obj = FPDFPage_GetObject(page, 38);
1079 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1080 bitmap = FPDFImageObj_GetBitmap(obj);
1081 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap));
1082 CompareBitmap(bitmap, 194, 119, "f9e24207ee1bc0db6c543d33a5f12ec5");
1083 FPDFBitmap_Destroy(bitmap);
1084 UnloadPage(page);
1085 }
1086
TEST_F(FPDFEditEmbeddertest,GetImageData)1087 TEST_F(FPDFEditEmbeddertest, GetImageData) {
1088 EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
1089 FPDF_PAGE page = LoadPage(0);
1090 ASSERT_TRUE(page);
1091 ASSERT_EQ(39, FPDFPage_CountObjects(page));
1092
1093 // Retrieve an image object with flate-encoded data stream.
1094 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 33);
1095 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1096
1097 // Check that the raw image data has the correct length and hash value.
1098 unsigned long len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
1099 std::vector<char> buf(len);
1100 EXPECT_EQ(4091u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
1101 EXPECT_EQ("f73802327d2e88e890f653961bcda81a",
1102 GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
1103
1104 // Check that the decoded image data has the correct length and hash value.
1105 len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
1106 buf.clear();
1107 buf.resize(len);
1108 EXPECT_EQ(28776u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
1109 EXPECT_EQ("cb3637934bb3b95a6e4ae1ea9eb9e56e",
1110 GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
1111
1112 // Retrieve an image obejct with DCTDecode-encoded data stream.
1113 obj = FPDFPage_GetObject(page, 37);
1114 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1115
1116 // Check that the raw image data has the correct length and hash value.
1117 len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
1118 buf.clear();
1119 buf.resize(len);
1120 EXPECT_EQ(4370u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
1121 EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
1122 GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
1123
1124 // Check that the decoded image data has the correct length and hash value,
1125 // which should be the same as those of the raw data, since this image is
1126 // encoded by a single DCTDecode filter and decoding is a noop.
1127 len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
1128 buf.clear();
1129 buf.resize(len);
1130 EXPECT_EQ(4370u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
1131 EXPECT_EQ("6aae1f3710335023a9e12191be66b64b",
1132 GenerateMD5Base16(reinterpret_cast<uint8_t*>(buf.data()), len));
1133
1134 UnloadPage(page);
1135 }
1136
TEST_F(FPDFEditEmbeddertest,DestroyPageObject)1137 TEST_F(FPDFEditEmbeddertest, DestroyPageObject) {
1138 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
1139 ASSERT_TRUE(rect);
1140
1141 // There should be no memory leaks with a call to FPDFPageObj_Destroy().
1142 FPDFPageObj_Destroy(rect);
1143 }
1144
TEST_F(FPDFEditEmbeddertest,GetImageFilters)1145 TEST_F(FPDFEditEmbeddertest, GetImageFilters) {
1146 EXPECT_TRUE(OpenDocument("embedded_images.pdf"));
1147 FPDF_PAGE page = LoadPage(0);
1148 ASSERT_TRUE(page);
1149
1150 // Verify that retrieving the filter of a non-image object would fail.
1151 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 32);
1152 ASSERT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1153 ASSERT_EQ(0, FPDFImageObj_GetImageFilterCount(obj));
1154 EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0));
1155
1156 // Verify the returned filter string for an image object with a single filter.
1157 obj = FPDFPage_GetObject(page, 33);
1158 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1159 ASSERT_EQ(1, FPDFImageObj_GetImageFilterCount(obj));
1160 unsigned long len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
1161 std::vector<char> buf(len);
1162 static constexpr char kFlateDecode[] = "FlateDecode";
1163 EXPECT_EQ(sizeof(kFlateDecode),
1164 FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
1165 EXPECT_STREQ(kFlateDecode, buf.data());
1166 EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0));
1167
1168 // Verify all the filters for an image object with a list of filters.
1169 obj = FPDFPage_GetObject(page, 38);
1170 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1171 ASSERT_EQ(2, FPDFImageObj_GetImageFilterCount(obj));
1172 len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
1173 buf.clear();
1174 buf.resize(len);
1175 static constexpr char kASCIIHexDecode[] = "ASCIIHexDecode";
1176 EXPECT_EQ(sizeof(kASCIIHexDecode),
1177 FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
1178 EXPECT_STREQ(kASCIIHexDecode, buf.data());
1179
1180 len = FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0);
1181 buf.clear();
1182 buf.resize(len);
1183 static constexpr char kDCTDecode[] = "DCTDecode";
1184 EXPECT_EQ(sizeof(kDCTDecode),
1185 FPDFImageObj_GetImageFilter(obj, 1, buf.data(), len));
1186 EXPECT_STREQ(kDCTDecode, buf.data());
1187
1188 UnloadPage(page);
1189 }
1190
TEST_F(FPDFEditEmbeddertest,GetImageMetadata)1191 TEST_F(FPDFEditEmbeddertest, GetImageMetadata) {
1192 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
1193 FPDF_PAGE page = LoadPage(0);
1194 ASSERT_TRUE(page);
1195
1196 // Check that getting the metadata of a null object would fail.
1197 FPDF_IMAGEOBJ_METADATA metadata;
1198 EXPECT_FALSE(FPDFImageObj_GetImageMetadata(nullptr, page, &metadata));
1199
1200 // Check that receiving the metadata with a null metadata object would fail.
1201 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page, 35);
1202 EXPECT_FALSE(FPDFImageObj_GetImageMetadata(obj, page, nullptr));
1203
1204 // Check that when retrieving an image object's metadata without passing in
1205 // |page|, all values are correct, with the last two being default values.
1206 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1207 ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, nullptr, &metadata));
1208 EXPECT_EQ(7, metadata.marked_content_id);
1209 EXPECT_EQ(92u, metadata.width);
1210 EXPECT_EQ(68u, metadata.height);
1211 EXPECT_NEAR(96.000000, metadata.horizontal_dpi, 0.001);
1212 EXPECT_NEAR(96.000000, metadata.vertical_dpi, 0.001);
1213 EXPECT_EQ(0u, metadata.bits_per_pixel);
1214 EXPECT_EQ(FPDF_COLORSPACE_UNKNOWN, metadata.colorspace);
1215
1216 // Verify the metadata of a bitmap image with indexed colorspace.
1217 ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
1218 EXPECT_EQ(7, metadata.marked_content_id);
1219 EXPECT_EQ(92u, metadata.width);
1220 EXPECT_EQ(68u, metadata.height);
1221 EXPECT_NEAR(96.000000, metadata.horizontal_dpi, 0.001);
1222 EXPECT_NEAR(96.000000, metadata.vertical_dpi, 0.001);
1223 EXPECT_EQ(1u, metadata.bits_per_pixel);
1224 EXPECT_EQ(FPDF_COLORSPACE_INDEXED, metadata.colorspace);
1225
1226 // Verify the metadata of an image with RGB colorspace.
1227 obj = FPDFPage_GetObject(page, 37);
1228 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
1229 ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page, &metadata));
1230 EXPECT_EQ(9, metadata.marked_content_id);
1231 EXPECT_EQ(126u, metadata.width);
1232 EXPECT_EQ(106u, metadata.height);
1233 EXPECT_NEAR(162.173752, metadata.horizontal_dpi, 0.001);
1234 EXPECT_NEAR(162.555878, metadata.vertical_dpi, 0.001);
1235 EXPECT_EQ(24u, metadata.bits_per_pixel);
1236 EXPECT_EQ(FPDF_COLORSPACE_DEVICERGB, metadata.colorspace);
1237
1238 UnloadPage(page);
1239 }
1240