blob: 727306a96e13d291c592550f0b3ccdb5c5986d33 [file] [log] [blame]
Tom Sepezd483eb42016-01-06 10:03:59 -08001// 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
Dan Sinclair85c8e7f2016-11-21 13:50:32 -05005#include <memory>
6#include <string>
Nicolas Penad03ca422017-03-06 13:54:33 -05007#include <utility>
Dan Sinclair85c8e7f2016-11-21 13:50:32 -05008
Nicolas Penabe90aae2017-02-27 10:41:41 -05009#include "core/fpdfapi/font/cpdf_font.h"
Nicolas Penaa4ad01f2017-02-15 16:26:48 -050010#include "core/fpdfapi/page/cpdf_page.h"
Nicolas Penabe90aae2017-02-27 10:41:41 -050011#include "core/fpdfapi/parser/cpdf_array.h"
Nicolas Penaa4ad01f2017-02-15 16:26:48 -050012#include "core/fpdfapi/parser/cpdf_dictionary.h"
Nicolas Penad03ca422017-03-06 13:54:33 -050013#include "core/fpdfapi/parser/cpdf_number.h"
Nicolas Penabe90aae2017-02-27 10:41:41 -050014#include "core/fpdfapi/parser/cpdf_stream.h"
Nicolas Pena0fc185e2017-02-08 12:13:20 -050015#include "core/fxcrt/fx_system.h"
Nicolas Penaa4ad01f2017-02-15 16:26:48 -050016#include "fpdfsdk/fsdk_define.h"
Nicolas Penab3161852017-05-02 14:12:50 -040017#include "public/cpp/fpdf_deleters.h"
Jane Liueda65252017-06-07 11:31:27 -040018#include "public/fpdf_annot.h"
Tom Sepezd483eb42016-01-06 10:03:59 -080019#include "public/fpdf_edit.h"
20#include "public/fpdfview.h"
21#include "testing/embedder_test.h"
Tom Sepez0aec19b2016-01-07 12:22:44 -080022#include "testing/gmock/include/gmock/gmock-matchers.h"
Tom Sepezd483eb42016-01-06 10:03:59 -080023#include "testing/gtest/include/gtest/gtest.h"
Tom Sepez0aec19b2016-01-07 12:22:44 -080024#include "testing/test_support.h"
Tom Sepezd483eb42016-01-06 10:03:59 -080025
Nicolas Penad03ca422017-03-06 13:54:33 -050026class FPDFEditEmbeddertest : public EmbedderTest, public TestSaver {
27 protected:
28 FPDF_DOCUMENT CreateNewDocument() {
29 document_ = FPDF_CreateNewDocument();
Tom Sepez41066c12017-05-18 09:28:49 -070030 cpdf_doc_ = CPDFDocumentFromFPDFDocument(document_);
Nicolas Penad03ca422017-03-06 13:54:33 -050031 return document_;
32 }
33
34 void CheckFontDescriptor(CPDF_Dictionary* font_dict,
35 int font_type,
36 bool bold,
37 bool italic,
38 uint32_t size,
39 const uint8_t* data) {
40 CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor");
41 ASSERT_TRUE(font_desc);
42 EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type"));
43 EXPECT_EQ(font_dict->GetStringFor("BaseFont"),
44 font_desc->GetStringFor("FontName"));
45
46 // Check that the font descriptor has the required keys according to spec
47 // 1.7 Table 5.19
48 ASSERT_TRUE(font_desc->KeyExist("Flags"));
49 int font_flags = font_desc->GetIntegerFor("Flags");
50 if (bold)
51 EXPECT_TRUE(font_flags & FXFONT_BOLD);
52 else
53 EXPECT_FALSE(font_flags & FXFONT_BOLD);
54 if (italic)
55 EXPECT_TRUE(font_flags & FXFONT_ITALIC);
56 else
57 EXPECT_FALSE(font_flags & FXFONT_ITALIC);
58 EXPECT_TRUE(font_flags & FXFONT_NONSYMBOLIC);
59 ASSERT_TRUE(font_desc->KeyExist("FontBBox"));
60 EXPECT_EQ(4U, font_desc->GetArrayFor("FontBBox")->GetCount());
61 EXPECT_TRUE(font_desc->KeyExist("ItalicAngle"));
62 EXPECT_TRUE(font_desc->KeyExist("Ascent"));
63 EXPECT_TRUE(font_desc->KeyExist("Descent"));
64 EXPECT_TRUE(font_desc->KeyExist("CapHeight"));
65 EXPECT_TRUE(font_desc->KeyExist("StemV"));
66 CFX_ByteString present("FontFile");
67 CFX_ByteString absent("FontFile2");
68 if (font_type == FPDF_FONT_TRUETYPE)
69 std::swap(present, absent);
70 EXPECT_TRUE(font_desc->KeyExist(present));
71 EXPECT_FALSE(font_desc->KeyExist(absent));
72
73 // Check that the font stream is the one that was provided
74 CPDF_Stream* font_stream = font_desc->GetStreamFor(present);
75 ASSERT_EQ(size, font_stream->GetRawSize());
76 uint8_t* stream_data = font_stream->GetRawData();
77 for (size_t j = 0; j < size; j++)
78 EXPECT_EQ(data[j], stream_data[j]) << " at byte " << j;
79 }
80
81 void CheckCompositeFontWidths(CPDF_Array* widths_array,
82 CPDF_Font* typed_font) {
83 // Check that W array is in a format that conforms to PDF spec 1.7 section
84 // "Glyph Metrics in CIDFonts" (these checks are not
85 // implementation-specific).
86 EXPECT_GT(widths_array->GetCount(), 1U);
87 int num_cids_checked = 0;
88 int cur_cid = 0;
89 for (size_t idx = 0; idx < widths_array->GetCount(); idx++) {
90 int cid = widths_array->GetNumberAt(idx);
91 EXPECT_GE(cid, cur_cid);
92 ASSERT_FALSE(++idx == widths_array->GetCount());
93 CPDF_Object* next = widths_array->GetObjectAt(idx);
94 if (next->IsArray()) {
95 // We are in the c [w1 w2 ...] case
96 CPDF_Array* arr = next->AsArray();
97 int cnt = static_cast<int>(arr->GetCount());
98 size_t inner_idx = 0;
99 for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) {
100 int width = arr->GetNumberAt(inner_idx++);
101 EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid "
102 << cur_cid;
103 }
104 num_cids_checked += cnt;
105 continue;
106 }
107 // Otherwise, are in the c_first c_last w case.
108 ASSERT_TRUE(next->IsNumber());
109 int last_cid = next->AsNumber()->GetInteger();
110 ASSERT_FALSE(++idx == widths_array->GetCount());
111 int width = widths_array->GetNumberAt(idx);
112 for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) {
113 EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid "
114 << cur_cid;
115 }
116 num_cids_checked += last_cid - cid + 1;
117 }
118 // Make sure we have a good amount of cids described
119 EXPECT_GT(num_cids_checked, 900);
120 }
121 CPDF_Document* cpdf_doc() { return cpdf_doc_; }
122
123 private:
124 CPDF_Document* cpdf_doc_;
125};
Tom Sepezd483eb42016-01-06 10:03:59 -0800126
etienneb7712c262016-04-26 08:13:45 -0700127namespace {
thestigdc7ec032016-11-21 15:32:52 -0800128
etienneb7712c262016-04-26 08:13:45 -0700129const char kExpectedPDF[] =
130 "%PDF-1.7\r\n"
131 "%\xA1\xB3\xC5\xD7\r\n"
132 "1 0 obj\r\n"
133 "<</Pages 2 0 R /Type/Catalog>>\r\n"
134 "endobj\r\n"
135 "2 0 obj\r\n"
136 "<</Count 1/Kids\\[ 4 0 R \\]/Type/Pages>>\r\n"
137 "endobj\r\n"
138 "3 0 obj\r\n"
139 "<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
140 "endobj\r\n"
141 "4 0 obj\r\n"
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400142 "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R "
143 "/Resources<</ExtGState<</FXE1 5 0 R >>>>"
Nicolas Penad9d6c292017-06-06 16:12:10 -0400144 "/Rotate 0/Type/Page"
etienneb7712c262016-04-26 08:13:45 -0700145 ">>\r\n"
146 "endobj\r\n"
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400147 "5 0 obj\r\n"
148 "<</BM/Normal/CA 1/ca 1>>\r\n"
149 "endobj\r\n"
etienneb7712c262016-04-26 08:13:45 -0700150 "xref\r\n"
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400151 "0 6\r\n"
etienneb7712c262016-04-26 08:13:45 -0700152 "0000000000 65535 f\r\n"
153 "0000000017 00000 n\r\n"
154 "0000000066 00000 n\r\n"
155 "0000000122 00000 n\r\n"
156 "0000000192 00000 n\r\n"
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400157 "0000000311 00000 n\r\n"
etienneb7712c262016-04-26 08:13:45 -0700158 "trailer\r\n"
159 "<<\r\n"
160 "/Root 1 0 R\r\n"
161 "/Info 3 0 R\r\n"
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400162 "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
etienneb7712c262016-04-26 08:13:45 -0700163 "startxref\r\n"
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400164 "354\r\n"
etienneb7712c262016-04-26 08:13:45 -0700165 "%%EOF\r\n";
thestigdc7ec032016-11-21 15:32:52 -0800166
etienneb7712c262016-04-26 08:13:45 -0700167} // namespace
168
Tom Sepezd483eb42016-01-06 10:03:59 -0800169TEST_F(FPDFEditEmbeddertest, EmptyCreation) {
170 EXPECT_TRUE(CreateEmptyDocument());
weili9b777de2016-08-19 16:19:46 -0700171 FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
Tom Sepezd483eb42016-01-06 10:03:59 -0800172 EXPECT_NE(nullptr, page);
Nicolas Penad9d6c292017-06-06 16:12:10 -0400173 // The FPDFPage_GenerateContent call should do nothing.
Tom Sepezd483eb42016-01-06 10:03:59 -0800174 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Tom Sepez0aec19b2016-01-07 12:22:44 -0800175 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
etienneb7712c262016-04-26 08:13:45 -0700176
Nicolas Penad9d6c292017-06-06 16:12:10 -0400177 EXPECT_THAT(GetString(), testing::MatchesRegex(std::string(
178 kExpectedPDF, sizeof(kExpectedPDF))));
weili9b777de2016-08-19 16:19:46 -0700179 FPDF_ClosePage(page);
Tom Sepezd483eb42016-01-06 10:03:59 -0800180}
thestigdc7ec032016-11-21 15:32:52 -0800181
182// Regression test for https://crbug.com/667012
183TEST_F(FPDFEditEmbeddertest, RasterizePDF) {
184 const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
185
186 // Get the bitmap for the original document/
187 FPDF_BITMAP orig_bitmap;
188 {
189 EXPECT_TRUE(OpenDocument("black.pdf"));
190 FPDF_PAGE orig_page = LoadPage(0);
191 EXPECT_NE(nullptr, orig_page);
192 orig_bitmap = RenderPage(orig_page);
193 CompareBitmap(orig_bitmap, 612, 792, kAllBlackMd5sum);
194 UnloadPage(orig_page);
195 }
196
197 // Create a new document from |orig_bitmap| and save it.
198 {
199 FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
200 FPDF_PAGE temp_page = FPDFPage_New(temp_doc, 0, 612, 792);
201
202 // Add the bitmap to an image object and add the image object to the output
203 // page.
Lei Zhangcbd89572017-03-15 17:35:47 -0700204 FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);
thestigdc7ec032016-11-21 15:32:52 -0800205 EXPECT_TRUE(FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap));
206 EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
207 FPDFPage_InsertObject(temp_page, temp_img);
208 EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
209 EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
210 FPDF_ClosePage(temp_page);
211 FPDF_CloseDocument(temp_doc);
212 }
213 FPDFBitmap_Destroy(orig_bitmap);
214
215 // Get the generated content. Make sure it is at least as big as the original
216 // PDF.
217 std::string new_file = GetString();
218 EXPECT_GT(new_file.size(), 923U);
219
220 // Read |new_file| in, and verify its rendered bitmap.
221 {
222 FPDF_FILEACCESS file_access;
223 memset(&file_access, 0, sizeof(file_access));
224 file_access.m_FileLen = new_file.size();
225 file_access.m_GetBlock = GetBlockFromString;
226 file_access.m_Param = &new_file;
227
228 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
229 EXPECT_EQ(1, FPDF_GetPageCount(document_));
230 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
231 EXPECT_NE(nullptr, new_page);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500232 FPDF_BITMAP new_bitmap = RenderPage(new_page);
thestigdc7ec032016-11-21 15:32:52 -0800233 CompareBitmap(new_bitmap, 612, 792, kAllBlackMd5sum);
234 FPDF_ClosePage(new_page);
235 FPDF_CloseDocument(new_doc);
236 FPDFBitmap_Destroy(new_bitmap);
237 }
238}
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500239
240TEST_F(FPDFEditEmbeddertest, AddPaths) {
241 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500242 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500243
244 // We will first add a red rectangle
245 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
246 ASSERT_NE(nullptr, red_rect);
247 // Expect false when trying to set colors out of range
248 EXPECT_FALSE(FPDFPath_SetStrokeColor(red_rect, 100, 100, 100, 300));
249 EXPECT_FALSE(FPDFPath_SetFillColor(red_rect, 200, 256, 200, 0));
250
251 // Fill rectangle with red and insert to the page
252 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
253 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
254 FPDFPage_InsertObject(page, red_rect);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500255 FPDF_BITMAP page_bitmap = RenderPage(page);
256 CompareBitmap(page_bitmap, 612, 792, "66d02eaa6181e2c069ce2ea99beda497");
257 FPDFBitmap_Destroy(page_bitmap);
258
259 // Now add to that a green rectangle with some medium alpha
260 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
261 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 128));
Miklos Vajnaed4705b2017-04-05 09:24:50 +0200262
Miklos Vajna1ef04c92017-05-08 18:14:19 +0200263 // Make sure the type of the rectangle is a path.
264 EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(green_rect));
265
Miklos Vajnaed4705b2017-04-05 09:24:50 +0200266 // Make sure we get back the same color we set previously.
267 unsigned int R;
268 unsigned int G;
269 unsigned int B;
270 unsigned int A;
271 EXPECT_TRUE(FPDFPath_GetFillColor(green_rect, &R, &G, &B, &A));
272 EXPECT_EQ(0U, R);
273 EXPECT_EQ(255U, G);
274 EXPECT_EQ(0U, B);
275 EXPECT_EQ(128U, A);
276
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500277 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
278 FPDFPage_InsertObject(page, green_rect);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500279 page_bitmap = RenderPage(page);
280 CompareBitmap(page_bitmap, 612, 792, "7b0b87604594e773add528fae567a558");
281 FPDFBitmap_Destroy(page_bitmap);
282
283 // Add a black triangle.
284 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
285 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 200));
286 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
287 EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
288 EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
289 EXPECT_TRUE(FPDFPath_Close(black_path));
290 FPDFPage_InsertObject(page, black_path);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500291 page_bitmap = RenderPage(page);
292 CompareBitmap(page_bitmap, 612, 792, "eadc8020a14dfcf091da2688733d8806");
293 FPDFBitmap_Destroy(page_bitmap);
294
295 // Now add a more complex blue path.
296 FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
297 EXPECT_TRUE(FPDFPath_SetFillColor(blue_path, 0, 0, 255, 255));
298 EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
299 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
300 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
301 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
302 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
303 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
304 EXPECT_TRUE(FPDFPath_Close(blue_path));
305 FPDFPage_InsertObject(page, blue_path);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500306 page_bitmap = RenderPage(page);
307 CompareBitmap(page_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
308 FPDFBitmap_Destroy(page_bitmap);
309
310 // Now save the result, closing the page and document
Nicolas Pena207b7272017-05-26 17:37:06 -0400311 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Nicolas Penad03ca422017-03-06 13:54:33 -0500312 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500313 FPDF_ClosePage(page);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500314 std::string new_file = GetString();
315
316 // Render the saved result
317 FPDF_FILEACCESS file_access;
318 memset(&file_access, 0, sizeof(file_access));
319 file_access.m_FileLen = new_file.size();
320 file_access.m_GetBlock = GetBlockFromString;
321 file_access.m_Param = &new_file;
322 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
323 ASSERT_NE(nullptr, new_doc);
324 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
325 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
326 ASSERT_NE(nullptr, new_page);
327 FPDF_BITMAP new_bitmap = RenderPage(new_page);
328 CompareBitmap(new_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
329 FPDFBitmap_Destroy(new_bitmap);
330 FPDF_ClosePage(new_page);
331 FPDF_CloseDocument(new_doc);
332}
333
334TEST_F(FPDFEditEmbeddertest, PathOnTopOfText) {
335 // Load document with some text
336 EXPECT_TRUE(OpenDocument("hello_world.pdf"));
337 FPDF_PAGE page = LoadPage(0);
338 EXPECT_NE(nullptr, page);
339
340 // Add an opaque rectangle on top of some of the text.
341 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
342 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
343 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
344 FPDFPage_InsertObject(page, red_rect);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500345
346 // Add a transparent triangle on top of other part of the text.
347 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
348 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 100));
349 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
350 EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
351 EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
352 EXPECT_TRUE(FPDFPath_Close(black_path));
353 FPDFPage_InsertObject(page, black_path);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500354
355 // Render and check the result. Text is slightly different on Mac.
356 FPDF_BITMAP bitmap = RenderPage(page);
357#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700358 const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500359#else
Lei Zhang3de50052017-03-29 21:02:13 -0700360 const char md5[] = "2fdfc5dda29374cfba4349362e38ebdb";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400361#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700362 CompareBitmap(bitmap, 200, 200, md5);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500363 FPDFBitmap_Destroy(bitmap);
364 UnloadPage(page);
365}
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500366
wileyryae858aa42017-05-31 14:49:05 -0500367TEST_F(FPDFEditEmbeddertest, EditOverExistingContent) {
368 // Load document with existing content
369 EXPECT_TRUE(OpenDocument("bug_717.pdf"));
370 FPDF_PAGE page = LoadPage(0);
371 EXPECT_NE(nullptr, page);
372
373 // Add a transparent rectangle on top of the existing content
374 FPDF_PAGEOBJECT red_rect2 = FPDFPageObj_CreateNewRect(90, 700, 25, 50);
375 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect2, 255, 0, 0, 100));
376 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect2, FPDF_FILLMODE_ALTERNATE, 0));
377 FPDFPage_InsertObject(page, red_rect2);
378
379 // Add an opaque rectangle on top of the existing content
380 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(115, 700, 25, 50);
381 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
382 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
383 FPDFPage_InsertObject(page, red_rect);
384
385 FPDF_BITMAP bitmap = RenderPage(page);
386 CompareBitmap(bitmap, 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
387 FPDFBitmap_Destroy(bitmap);
388 EXPECT_TRUE(FPDFPage_GenerateContent(page));
389
390 // Now save the result, closing the page and document
391 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
392 FPDF_ClosePage(page);
393
394 // Render the saved result
395 std::string new_file = GetString();
396 FPDF_FILEACCESS file_access;
397 memset(&file_access, 0, sizeof(file_access));
398 file_access.m_FileLen = new_file.size();
399 file_access.m_GetBlock = GetBlockFromString;
400 file_access.m_Param = &new_file;
401 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
402 ASSERT_NE(nullptr, new_doc);
403 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
404 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
405 ASSERT_NE(nullptr, new_page);
406 FPDF_BITMAP new_bitmap = RenderPage(new_page);
407 CompareBitmap(new_bitmap, 612, 792, "ad04e5bd0f471a9a564fb034bd0fb073");
408 FPDFBitmap_Destroy(new_bitmap);
409
410 ClearString();
411 // Add another opaque rectangle on top of the existing content
412 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(150, 700, 25, 50);
413 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 255));
414 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_ALTERNATE, 0));
415 FPDFPage_InsertObject(new_page, green_rect);
416
417 // Add another transparent rectangle on top of existing content
418 FPDF_PAGEOBJECT green_rect2 = FPDFPageObj_CreateNewRect(175, 700, 25, 50);
419 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect2, 0, 255, 0, 100));
420 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect2, FPDF_FILLMODE_ALTERNATE, 0));
421 FPDFPage_InsertObject(new_page, green_rect2);
422 new_bitmap = RenderPage(new_page);
423 CompareBitmap(new_bitmap, 612, 792, "4b5b00f824620f8c9b8801ebb98e1cdd");
424 FPDFBitmap_Destroy(new_bitmap);
425 EXPECT_TRUE(FPDFPage_GenerateContent(new_page));
426
427 // Now save the result, closing the page and document
428 EXPECT_TRUE(FPDF_SaveAsCopy(new_doc, this, 0));
429 FPDF_ClosePage(new_page);
430 FPDF_CloseDocument(new_doc);
431
432 // Render the saved result
433 new_file = GetString();
434 memset(&file_access, 0, sizeof(file_access));
435 file_access.m_FileLen = new_file.size();
436 file_access.m_GetBlock = GetBlockFromString;
437 file_access.m_Param = &new_file;
438 new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
439 ASSERT_NE(nullptr, new_doc);
440 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
441 new_page = FPDF_LoadPage(new_doc, 0);
442 ASSERT_NE(nullptr, new_page);
443 new_bitmap = RenderPage(new_page);
444 CompareBitmap(new_bitmap, 612, 792, "4b5b00f824620f8c9b8801ebb98e1cdd");
445 FPDFBitmap_Destroy(new_bitmap);
446
447 FPDF_ClosePage(new_page);
448 FPDF_CloseDocument(new_doc);
449}
450
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500451TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) {
452 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500453 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500454
455 // Add a large stroked rectangle (fill color should not affect it).
456 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
457 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 255));
458 EXPECT_TRUE(FPDFPath_SetStrokeColor(rect, 0, 255, 0, 255));
459 EXPECT_TRUE(FPDFPath_SetStrokeWidth(rect, 15.0f));
460 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
461 FPDFPage_InsertObject(page, rect);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500462 FPDF_BITMAP page_bitmap = RenderPage(page);
463 CompareBitmap(page_bitmap, 612, 792, "64bd31f862a89e0a9e505a5af6efd506");
464 FPDFBitmap_Destroy(page_bitmap);
465
466 // Add crossed-checkmark
467 FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
468 EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
469 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
470 EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
471 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
472 EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 128, 128, 128, 180));
473 EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
474 EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
475 FPDFPage_InsertObject(page, check);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500476 page_bitmap = RenderPage(page);
477 CompareBitmap(page_bitmap, 612, 792, "4b6f3b9d25c4e194821217d5016c3724");
478 FPDFBitmap_Destroy(page_bitmap);
479
480 // Add stroked and filled oval-ish path.
481 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
482 EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
483 EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
484 EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
485 EXPECT_TRUE(FPDFPath_Close(path));
486 EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 128, 128, 100));
487 EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 128, 200, 128, 150));
488 EXPECT_TRUE(FPDFPath_SetStrokeWidth(path, 10.5f));
489 EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
490 FPDFPage_InsertObject(page, path);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500491 page_bitmap = RenderPage(page);
492 CompareBitmap(page_bitmap, 612, 792, "ff3e6a22326754944cc6e56609acd73b");
493 FPDFBitmap_Destroy(page_bitmap);
494 FPDF_ClosePage(page);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500495}
Nicolas Pena49058402017-02-14 18:26:20 -0500496
497TEST_F(FPDFEditEmbeddertest, AddStandardFontText) {
498 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500499 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena49058402017-02-14 18:26:20 -0500500
501 // Add some text to the page
Nicolas Penab3161852017-05-02 14:12:50 -0400502 FPDF_PAGEOBJECT text_object1 =
503 FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
504 EXPECT_TRUE(text_object1);
505 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text1 =
506 GetFPDFWideString(L"I'm at the bottom of the page");
507 EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get()));
508 FPDFPageObj_Transform(text_object1, 1, 0, 0, 1, 20, 20);
509 FPDFPage_InsertObject(page, text_object1);
Nicolas Pena49058402017-02-14 18:26:20 -0500510 FPDF_BITMAP page_bitmap = RenderPage(page);
511#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700512 const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
Nicolas Pena49058402017-02-14 18:26:20 -0500513#else
Lei Zhang3de50052017-03-29 21:02:13 -0700514 const char md5[] = "6e8a9b0682f60fd3ff1bf087b093d30d";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400515#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700516 CompareBitmap(page_bitmap, 612, 792, md5);
Nicolas Pena49058402017-02-14 18:26:20 -0500517 FPDFBitmap_Destroy(page_bitmap);
518
519 // Try another font
Nicolas Penab3161852017-05-02 14:12:50 -0400520 FPDF_PAGEOBJECT text_object2 =
Nicolas Penad03ca422017-03-06 13:54:33 -0500521 FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
Nicolas Penab3161852017-05-02 14:12:50 -0400522 EXPECT_TRUE(text_object2);
523 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
524 GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold.");
525 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
526 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600);
527 FPDFPage_InsertObject(page, text_object2);
Nicolas Pena49058402017-02-14 18:26:20 -0500528 page_bitmap = RenderPage(page);
529#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700530 const char md5_2[] = "a5c4ace4c6f27644094813fe1441a21c";
Lei Zhang3de50052017-03-29 21:02:13 -0700531#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
532 const char md5_2[] = "56863642d4d8b418cfd810fdb5a5d92f";
Nicolas Pena49058402017-02-14 18:26:20 -0500533#else
Lei Zhang3de50052017-03-29 21:02:13 -0700534 const char md5_2[] = "0c83875429688bda45a55a692d5aa781";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400535#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700536 CompareBitmap(page_bitmap, 612, 792, md5_2);
Nicolas Pena49058402017-02-14 18:26:20 -0500537 FPDFBitmap_Destroy(page_bitmap);
538
539 // And some randomly transformed text
Nicolas Penab3161852017-05-02 14:12:50 -0400540 FPDF_PAGEOBJECT text_object3 =
Nicolas Penad03ca422017-03-06 13:54:33 -0500541 FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
Nicolas Penab3161852017-05-02 14:12:50 -0400542 EXPECT_TRUE(text_object3);
543 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text3 =
544 GetFPDFWideString(L"Can you read me? <:)>");
545 EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get()));
546 FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200);
547 FPDFPage_InsertObject(page, text_object3);
Nicolas Pena49058402017-02-14 18:26:20 -0500548 page_bitmap = RenderPage(page);
549#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700550 const char md5_3[] = "40b3ef04f915ff4c4208948001763544";
Lei Zhang3de50052017-03-29 21:02:13 -0700551#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
Lei Zhangb8e89e32017-05-02 11:53:08 -0700552 const char md5_3[] = "d69d419def35a098d9c10f8317d6709b";
Nicolas Pena49058402017-02-14 18:26:20 -0500553#else
Lei Zhangb8e89e32017-05-02 11:53:08 -0700554 const char md5_3[] = "8b300d3c6dfc12fa9af97e12ed5bd80a";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400555#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700556 CompareBitmap(page_bitmap, 612, 792, md5_3);
Nicolas Pena49058402017-02-14 18:26:20 -0500557 FPDFBitmap_Destroy(page_bitmap);
558
559 // TODO(npm): Why are there issues with text rotated by 90 degrees?
560 // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
561 FPDF_ClosePage(page);
Nicolas Pena49058402017-02-14 18:26:20 -0500562}
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500563
Nicolas Pena603a31d2017-06-14 11:41:18 -0400564TEST_F(FPDFEditEmbeddertest, GraphicsData) {
565 // New page
566 std::unique_ptr<void, FPDFPageDeleter> page(
567 FPDFPage_New(CreateNewDocument(), 0, 612, 792));
568
569 // Create a rect with nontrivial graphics
570 FPDF_PAGEOBJECT rect1 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
571 FPDFPageObj_SetBlendMode(rect1, "Color");
572 FPDFPage_InsertObject(page.get(), rect1);
573 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
574
575 // Check that the ExtGState was created
576 CPDF_Page* the_page = CPDFPageFromFPDFPage(page.get());
577 CPDF_Dictionary* graphics_dict =
578 the_page->m_pResources->GetDictFor("ExtGState");
579 ASSERT_TRUE(graphics_dict);
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400580 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
Nicolas Pena603a31d2017-06-14 11:41:18 -0400581
582 // Add a text object causing no change to the graphics dictionary
583 FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
584 // Only alpha, the last component, matters for the graphics dictionary. And
585 // the default value is 255.
586 EXPECT_TRUE(FPDFText_SetFillColor(text1, 100, 100, 100, 255));
587 FPDFPage_InsertObject(page.get(), text1);
588 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400589 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
Nicolas Pena603a31d2017-06-14 11:41:18 -0400590
591 // Add a text object increasing the size of the graphics dictionary
592 FPDF_PAGEOBJECT text2 =
593 FPDFPageObj_NewTextObj(document(), "Times-Roman", 12.0f);
594 FPDFPage_InsertObject(page.get(), text2);
595 FPDFPageObj_SetBlendMode(text2, "Darken");
596 EXPECT_TRUE(FPDFText_SetFillColor(text2, 0, 0, 255, 150));
597 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400598 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
Nicolas Pena603a31d2017-06-14 11:41:18 -0400599
600 // Add a path that should reuse graphics
Nicolas Penace67be42017-06-14 14:52:49 -0400601 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
Nicolas Pena603a31d2017-06-14 11:41:18 -0400602 FPDFPageObj_SetBlendMode(path, "Darken");
603 EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 200, 100, 150));
604 FPDFPage_InsertObject(page.get(), path);
605 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400606 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
Nicolas Pena603a31d2017-06-14 11:41:18 -0400607
608 // Add a rect increasing the size of the graphics dictionary
609 FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
610 FPDFPageObj_SetBlendMode(rect2, "Darken");
611 EXPECT_TRUE(FPDFPath_SetFillColor(rect2, 0, 0, 255, 150));
612 EXPECT_TRUE(FPDFPath_SetStrokeColor(rect2, 0, 0, 0, 200));
613 FPDFPage_InsertObject(page.get(), rect2);
614 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400615 EXPECT_EQ(4, static_cast<int>(graphics_dict->GetCount()));
Nicolas Pena603a31d2017-06-14 11:41:18 -0400616}
617
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500618TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
619 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500620 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500621
622 // Add a red rectangle with some non-default alpha
623 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
624 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 128));
625 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
626 FPDFPage_InsertObject(page, rect);
627 EXPECT_TRUE(FPDFPage_GenerateContent(page));
628
629 // Check the ExtGState
630 CPDF_Page* the_page = CPDFPageFromFPDFPage(page);
631 CPDF_Dictionary* graphics_dict =
632 the_page->m_pResources->GetDictFor("ExtGState");
633 ASSERT_TRUE(graphics_dict);
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400634 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500635
636 // Check the bitmap
637 FPDF_BITMAP page_bitmap = RenderPage(page);
638 CompareBitmap(page_bitmap, 612, 792, "5384da3406d62360ffb5cac4476fff1c");
639 FPDFBitmap_Destroy(page_bitmap);
640
641 // Never mind, my new favorite color is blue, increase alpha
642 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 0, 0, 255, 180));
643 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400644 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500645
646 // Check that bitmap displays changed content
647 page_bitmap = RenderPage(page);
648 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
649 FPDFBitmap_Destroy(page_bitmap);
650
651 // And now generate, without changes
652 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400653 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500654 page_bitmap = RenderPage(page);
655 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
656 FPDFBitmap_Destroy(page_bitmap);
657
658 // Add some text to the page
Nicolas Penab3161852017-05-02 14:12:50 -0400659 FPDF_PAGEOBJECT text_object =
660 FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
661 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
662 GetFPDFWideString(L"Something something #text# something");
663 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
664 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300);
665 FPDFPage_InsertObject(page, text_object);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500666 EXPECT_TRUE(FPDFPage_GenerateContent(page));
667 CPDF_Dictionary* font_dict = the_page->m_pResources->GetDictFor("Font");
668 ASSERT_TRUE(font_dict);
669 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
670
671 // Generate yet again, check dicts are reasonably sized
672 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400673 EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500674 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
675 FPDF_ClosePage(page);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500676}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500677
Nicolas Penad03ca422017-03-06 13:54:33 -0500678TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) {
679 CreateNewDocument();
680 // TODO(npm): use other fonts after disallowing loading any font as any type
681 const CPDF_Font* stock_font =
682 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
Lei Zhangd74da7b2017-05-04 13:30:29 -0700683 const uint8_t* data = stock_font->GetFont()->GetFontData();
684 const uint32_t size = stock_font->GetFont()->GetSize();
Nicolas Penab3161852017-05-02 14:12:50 -0400685 std::unique_ptr<void, FPDFFontDeleter> font(
686 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false));
687 ASSERT_TRUE(font.get());
Nicolas Pena46abb662017-05-17 17:23:22 -0400688 CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
Nicolas Penad03ca422017-03-06 13:54:33 -0500689 EXPECT_TRUE(typed_font->IsType1Font());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500690
Nicolas Penad03ca422017-03-06 13:54:33 -0500691 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
Nicolas Penabe90aae2017-02-27 10:41:41 -0500692 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
693 EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
694 EXPECT_EQ("Times New Roman Bold", font_dict->GetStringFor("BaseFont"));
695 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
696 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
697 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
Nicolas Penad03ca422017-03-06 13:54:33 -0500698 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
699
Nicolas Penabe90aae2017-02-27 10:41:41 -0500700 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
Nicolas Penad03ca422017-03-06 13:54:33 -0500701 ASSERT_TRUE(widths_array);
702 ASSERT_EQ(224U, widths_array->GetCount());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500703 EXPECT_EQ(250, widths_array->GetNumberAt(0));
Nicolas Penad03ca422017-03-06 13:54:33 -0500704 EXPECT_EQ(569, widths_array->GetNumberAt(11));
705 EXPECT_EQ(500, widths_array->GetNumberAt(223));
706 CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, size, data);
707}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500708
Nicolas Penad03ca422017-03-06 13:54:33 -0500709TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) {
710 CreateNewDocument();
711 const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
Lei Zhangd74da7b2017-05-04 13:30:29 -0700712 const uint8_t* data = stock_font->GetFont()->GetFontData();
713 const uint32_t size = stock_font->GetFont()->GetSize();
Nicolas Penab3161852017-05-02 14:12:50 -0400714 std::unique_ptr<void, FPDFFontDeleter> font(
715 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false));
716 ASSERT_TRUE(font.get());
Nicolas Pena46abb662017-05-17 17:23:22 -0400717 CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
Nicolas Penad03ca422017-03-06 13:54:33 -0500718 EXPECT_TRUE(typed_font->IsTrueTypeFont());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500719
Nicolas Penad03ca422017-03-06 13:54:33 -0500720 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
721 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
722 EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
723 EXPECT_EQ("Courier New", font_dict->GetStringFor("BaseFont"));
724 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
725 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
726 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
727 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
Nicolas Penabe90aae2017-02-27 10:41:41 -0500728
Nicolas Penad03ca422017-03-06 13:54:33 -0500729 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
730 ASSERT_TRUE(widths_array);
731 ASSERT_EQ(224U, widths_array->GetCount());
732 EXPECT_EQ(600, widths_array->GetNumberAt(33));
733 EXPECT_EQ(600, widths_array->GetNumberAt(74));
734 EXPECT_EQ(600, widths_array->GetNumberAt(223));
735 CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, size, data);
736}
737
738TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) {
739 CreateNewDocument();
740 const CPDF_Font* stock_font =
741 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
Lei Zhangd74da7b2017-05-04 13:30:29 -0700742 const uint8_t* data = stock_font->GetFont()->GetFontData();
743 const uint32_t size = stock_font->GetFont()->GetSize();
Nicolas Penab3161852017-05-02 14:12:50 -0400744 std::unique_ptr<void, FPDFFontDeleter> font(
745 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1));
746 ASSERT_TRUE(font.get());
Nicolas Pena46abb662017-05-17 17:23:22 -0400747 CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
Nicolas Penad03ca422017-03-06 13:54:33 -0500748 EXPECT_TRUE(typed_font->IsCIDFont());
749
750 // Check font dictionary entries
751 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
752 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
753 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
754 EXPECT_EQ("Times New Roman-Identity-H", font_dict->GetStringFor("BaseFont"));
755 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
756 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
757 ASSERT_TRUE(descendant_array);
758 EXPECT_EQ(1U, descendant_array->GetCount());
759
760 // Check the CIDFontDict
761 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
762 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
763 EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
764 EXPECT_EQ("Times New Roman", cidfont_dict->GetStringFor("BaseFont"));
765 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
766 ASSERT_TRUE(cidinfo_dict);
767 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
768 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
769 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
770 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, size, data);
771
772 // Check widths
773 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
774 ASSERT_TRUE(widths_array);
Nicolas Penaf45ade32017-05-03 10:23:49 -0400775 EXPECT_GT(widths_array->GetCount(), 1U);
Nicolas Penad03ca422017-03-06 13:54:33 -0500776 CheckCompositeFontWidths(widths_array, typed_font);
777}
778
779TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) {
780 CreateNewDocument();
781 const CPDF_Font* stock_font =
782 CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
Lei Zhangd74da7b2017-05-04 13:30:29 -0700783 const uint8_t* data = stock_font->GetFont()->GetFontData();
784 const uint32_t size = stock_font->GetFont()->GetSize();
Nicolas Penad03ca422017-03-06 13:54:33 -0500785
Nicolas Penab3161852017-05-02 14:12:50 -0400786 std::unique_ptr<void, FPDFFontDeleter> font(
787 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1));
788 ASSERT_TRUE(font.get());
Nicolas Pena46abb662017-05-17 17:23:22 -0400789 CPDF_Font* typed_font = static_cast<CPDF_Font*>(font.get());
Nicolas Penad03ca422017-03-06 13:54:33 -0500790 EXPECT_TRUE(typed_font->IsCIDFont());
791
792 // Check font dictionary entries
793 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
794 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
795 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
796 EXPECT_EQ("Arial Italic", font_dict->GetStringFor("BaseFont"));
797 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
798 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
799 ASSERT_TRUE(descendant_array);
800 EXPECT_EQ(1U, descendant_array->GetCount());
801
802 // Check the CIDFontDict
803 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
804 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
805 EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
806 EXPECT_EQ("Arial Italic", cidfont_dict->GetStringFor("BaseFont"));
807 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
808 ASSERT_TRUE(cidinfo_dict);
809 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
810 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
811 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
812 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, size,
813 data);
814
815 // Check widths
816 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
817 ASSERT_TRUE(widths_array);
818 CheckCompositeFontWidths(widths_array, typed_font);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500819}
rbpotterce8e51e2017-04-28 12:42:47 -0700820
821TEST_F(FPDFEditEmbeddertest, NormalizeNegativeRotation) {
822 // Load document with a -90 degree rotation
823 EXPECT_TRUE(OpenDocument("bug_713197.pdf"));
824 FPDF_PAGE page = LoadPage(0);
825 EXPECT_NE(nullptr, page);
826
827 EXPECT_EQ(3, FPDFPage_GetRotation(page));
828 UnloadPage(page);
829}
Nicolas Penab3161852017-05-02 14:12:50 -0400830
831TEST_F(FPDFEditEmbeddertest, AddTrueTypeFontText) {
832 // Start with a blank page
833 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
834 {
835 const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
Lei Zhangd74da7b2017-05-04 13:30:29 -0700836 const uint8_t* data = stock_font->GetFont()->GetFontData();
837 const uint32_t size = stock_font->GetFont()->GetSize();
Nicolas Penab3161852017-05-02 14:12:50 -0400838 std::unique_ptr<void, FPDFFontDeleter> font(
839 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 0));
840 ASSERT_TRUE(font.get());
841
842 // Add some text to the page
843 FPDF_PAGEOBJECT text_object =
844 FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
845 EXPECT_TRUE(text_object);
846 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
847 GetFPDFWideString(L"I am testing my loaded font, WEE.");
848 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
849 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
850 FPDFPage_InsertObject(page, text_object);
Nicolas Penab3161852017-05-02 14:12:50 -0400851 FPDF_BITMAP page_bitmap = RenderPage(page);
852#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
853 const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
854#else
855 const char md5[] = "28e5b10743660dcdfd1618db47b39d32";
856#endif // _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
857 CompareBitmap(page_bitmap, 612, 792, md5);
858 FPDFBitmap_Destroy(page_bitmap);
859
860 // Add some more text, same font
861 FPDF_PAGEOBJECT text_object2 =
862 FPDFPageObj_CreateTextObj(document(), font.get(), 15.0f);
863 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
864 GetFPDFWideString(L"Bigger font size");
865 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
866 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 200, 200);
867 FPDFPage_InsertObject(page, text_object2);
Nicolas Penab3161852017-05-02 14:12:50 -0400868 }
869 FPDF_BITMAP page_bitmap2 = RenderPage(page);
870#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
871 const char md5_2[] = "8eded4193ff1f0f77b8b600a825e97ea";
872#else
873 const char md5_2[] = "a068eef4110d607f77c87ea8340fa2a5";
874#endif // _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
875 CompareBitmap(page_bitmap2, 612, 792, md5_2);
876 FPDFBitmap_Destroy(page_bitmap2);
877
Nicolas Pena207b7272017-05-26 17:37:06 -0400878 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Nicolas Penab3161852017-05-02 14:12:50 -0400879 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
880 FPDF_ClosePage(page);
881 std::string new_file = GetString();
882
883 // Render the saved result
884 FPDF_FILEACCESS file_access;
885 memset(&file_access, 0, sizeof(file_access));
886 file_access.m_FileLen = new_file.size();
887 file_access.m_GetBlock = GetBlockFromString;
888 file_access.m_Param = &new_file;
889 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
890 ASSERT_NE(nullptr, new_doc);
891 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
892 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
893 ASSERT_NE(nullptr, new_page);
894 FPDF_BITMAP new_bitmap = RenderPage(new_page);
895 CompareBitmap(new_bitmap, 612, 792, md5_2);
896 FPDFBitmap_Destroy(new_bitmap);
897 FPDF_ClosePage(new_page);
898 FPDF_CloseDocument(new_doc);
899}
Nicolas Penaf45ade32017-05-03 10:23:49 -0400900
Jane Liueda65252017-06-07 11:31:27 -0400901TEST_F(FPDFEditEmbeddertest, TransformAnnot) {
902 // Open a file with one annotation and load its first page.
903 ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
904 FPDF_PAGE page = FPDF_LoadPage(document(), 0);
905 ASSERT_TRUE(page);
906
907 // Add an underline annotation to the page without specifying its rectangle.
Jane Liud60e9ad2017-06-26 11:28:36 -0400908 FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE);
909 ASSERT_TRUE(annot);
Jane Liueda65252017-06-07 11:31:27 -0400910
911 // FPDFPage_TransformAnnots() should run without errors when modifying
912 // annotation rectangles.
913 FPDFPage_TransformAnnots(page, 1, 2, 3, 4, 5, 6);
914
Jane Liud60e9ad2017-06-26 11:28:36 -0400915 FPDFPage_CloseAnnot(annot);
Jane Liueda65252017-06-07 11:31:27 -0400916 UnloadPage(page);
917}
918
Nicolas Penaf45ade32017-05-03 10:23:49 -0400919// TODO(npm): Add tests using Japanese fonts in other OS.
920#if _FXM_PLATFORM_ == _FXM_PLATFORM_LINUX_
921TEST_F(FPDFEditEmbeddertest, AddCIDFontText) {
922 // Start with a blank page
923 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
924 CFX_Font CIDfont;
925 {
926 // First, get the data from the font
927 CIDfont.LoadSubst("IPAGothic", 1, 0, 400, 0, 932, 0);
928 EXPECT_EQ("IPAGothic", CIDfont.GetFaceName());
929 const uint8_t* data = CIDfont.GetFontData();
930 const uint32_t size = CIDfont.GetSize();
931
932 // Load the data into a FPDF_Font.
933 std::unique_ptr<void, FPDFFontDeleter> font(
934 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1));
935 ASSERT_TRUE(font.get());
936
937 // Add some text to the page
938 FPDF_PAGEOBJECT text_object =
939 FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
940 ASSERT_TRUE(text_object);
941 std::wstring wstr = L"ABCDEFGhijklmnop.";
942 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text =
943 GetFPDFWideString(wstr);
944 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
945 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 200);
946 FPDFPage_InsertObject(page, text_object);
947
948 // And add some Japanese characters
949 FPDF_PAGEOBJECT text_object2 =
950 FPDFPageObj_CreateTextObj(document(), font.get(), 18.0f);
951 ASSERT_TRUE(text_object2);
952 std::wstring wstr2 =
953 L"\u3053\u3093\u306B\u3061\u306f\u4e16\u754C\u3002\u3053\u3053\u306B1"
954 L"\u756A";
955 std::unique_ptr<unsigned short, pdfium::FreeDeleter> text2 =
956 GetFPDFWideString(wstr2);
957 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
958 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 500);
959 FPDFPage_InsertObject(page, text_object2);
960 }
961
Nicolas Pena207b7272017-05-26 17:37:06 -0400962 // Check that the text renders properly.
Nicolas Penaf45ade32017-05-03 10:23:49 -0400963 FPDF_BITMAP page_bitmap = RenderPage(page);
964 const char md5[] = "2bc6c1aaa2252e73246a75775ccf38c2";
965 CompareBitmap(page_bitmap, 612, 792, md5);
966 FPDFBitmap_Destroy(page_bitmap);
967
968 // Save the document, close the page.
Nicolas Pena207b7272017-05-26 17:37:06 -0400969 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Nicolas Penaf45ade32017-05-03 10:23:49 -0400970 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
971 FPDF_ClosePage(page);
972 std::string new_file = GetString();
973
974 // Render the saved result
975 FPDF_FILEACCESS file_access;
976 memset(&file_access, 0, sizeof(file_access));
977 file_access.m_FileLen = new_file.size();
978 file_access.m_GetBlock = GetBlockFromString;
979 file_access.m_Param = &new_file;
980 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
981 ASSERT_NE(nullptr, new_doc);
982 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
983 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
984 ASSERT_NE(nullptr, new_page);
985 FPDF_BITMAP new_bitmap = RenderPage(new_page);
986 CompareBitmap(new_bitmap, 612, 792, md5);
987 FPDFBitmap_Destroy(new_bitmap);
988 FPDF_ClosePage(new_page);
989 FPDF_CloseDocument(new_doc);
990}
991#endif // _FXM_PLATFORM_ == _FXM_PLATFORM_LINUX_
Nicolas Pena9ba8fbc2017-06-28 15:31:56 -0400992
993TEST_F(FPDFEditEmbeddertest, SaveAndRender) {
994 const char embMD5[] = "3c20472b0552c0c22b88ab1ed8c6202b";
995 {
996 EXPECT_TRUE(OpenDocument("bug_779.pdf"));
997 FPDF_PAGE page = LoadPage(0);
998 ASSERT_NE(nullptr, page);
999
1000 // Now add a more complex blue path.
1001 FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20);
1002 EXPECT_TRUE(FPDFPath_SetFillColor(green_path, 0, 255, 0, 200));
1003 // TODO(npm): stroking will cause the MD5s to differ.
1004 EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0));
1005 EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63));
1006 EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90));
1007 EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 133));
1008 EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 33));
1009 EXPECT_TRUE(FPDFPath_BezierTo(green_path, 38, 33, 39, 36, 40, 40));
1010 EXPECT_TRUE(FPDFPath_Close(green_path));
1011 FPDFPage_InsertObject(page, green_path);
1012 FPDF_BITMAP page_bitmap = RenderPage(page);
1013 CompareBitmap(page_bitmap, 612, 792, embMD5);
1014 FPDFBitmap_Destroy(page_bitmap);
1015
1016 // Now save the result, closing the page and document
1017 EXPECT_TRUE(FPDFPage_GenerateContent(page));
1018 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1019 UnloadPage(page);
1020 }
1021
1022 // Render the saved result
1023 std::string new_file = GetString();
1024 FPDF_FILEACCESS file_access;
1025 memset(&file_access, 0, sizeof(file_access));
1026 file_access.m_FileLen = new_file.size();
1027 file_access.m_GetBlock = GetBlockFromString;
1028 file_access.m_Param = &new_file;
1029 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
1030 ASSERT_NE(nullptr, new_doc);
1031 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
1032 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
1033 ASSERT_NE(nullptr, new_page);
1034 FPDF_BITMAP new_bitmap = RenderPage(new_page);
1035 CompareBitmap(new_bitmap, 612, 792, embMD5);
1036 FPDFBitmap_Destroy(new_bitmap);
1037 FPDF_ClosePage(new_page);
1038 FPDF_CloseDocument(new_doc);
1039}