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 27class FPDFEditEmbeddertest : public EmbedderTest { 28 protected: 29 FPDF_DOCUMENT CreateNewDocument() { 30 document_ = FPDF_CreateNewDocument(); 31 cpdf_doc_ = CPDFDocumentFromFPDFDocument(document_); 32 return document_; 33 } 34 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 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 } 129 CPDF_Document* cpdf_doc() { return cpdf_doc_; } 130 131 private: 132 CPDF_Document* cpdf_doc_; 133}; 134 135namespace { 136 137const 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 177TEST_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 191TEST_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 229TEST_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 372TEST_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 399TEST_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 432TEST_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 491TEST_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 537TEST_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 604TEST_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 658TEST_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 718TEST_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 749TEST_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 778TEST_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 819TEST_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 861TEST_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 871TEST_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 925TEST_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_ 945TEST_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 1001TEST_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 1033TEST_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 1087TEST_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 1137TEST_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 1145TEST_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 1191TEST_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