• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <memory>
6 #include <string>
7 #include <utility>
8 #include <vector>
9 
10 #include "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