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