blob: a54a9b93afe9424bb7945d3fbafe22a630fd81a7 [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"
Tom Sepezd483eb42016-01-06 10:03:59 -080017#include "public/fpdf_edit.h"
18#include "public/fpdfview.h"
19#include "testing/embedder_test.h"
Tom Sepez0aec19b2016-01-07 12:22:44 -080020#include "testing/gmock/include/gmock/gmock-matchers.h"
Tom Sepezd483eb42016-01-06 10:03:59 -080021#include "testing/gtest/include/gtest/gtest.h"
Tom Sepez0aec19b2016-01-07 12:22:44 -080022#include "testing/test_support.h"
Tom Sepezd483eb42016-01-06 10:03:59 -080023
Nicolas Penad03ca422017-03-06 13:54:33 -050024class FPDFEditEmbeddertest : public EmbedderTest, public TestSaver {
25 protected:
26 FPDF_DOCUMENT CreateNewDocument() {
27 document_ = FPDF_CreateNewDocument();
28 cpdf_doc_ = reinterpret_cast<CPDF_Document*>(document_);
29 return document_;
30 }
31
32 void CheckFontDescriptor(CPDF_Dictionary* font_dict,
33 int font_type,
34 bool bold,
35 bool italic,
36 uint32_t size,
37 const uint8_t* data) {
38 CPDF_Dictionary* font_desc = font_dict->GetDictFor("FontDescriptor");
39 ASSERT_TRUE(font_desc);
40 EXPECT_EQ("FontDescriptor", font_desc->GetStringFor("Type"));
41 EXPECT_EQ(font_dict->GetStringFor("BaseFont"),
42 font_desc->GetStringFor("FontName"));
43
44 // Check that the font descriptor has the required keys according to spec
45 // 1.7 Table 5.19
46 ASSERT_TRUE(font_desc->KeyExist("Flags"));
47 int font_flags = font_desc->GetIntegerFor("Flags");
48 if (bold)
49 EXPECT_TRUE(font_flags & FXFONT_BOLD);
50 else
51 EXPECT_FALSE(font_flags & FXFONT_BOLD);
52 if (italic)
53 EXPECT_TRUE(font_flags & FXFONT_ITALIC);
54 else
55 EXPECT_FALSE(font_flags & FXFONT_ITALIC);
56 EXPECT_TRUE(font_flags & FXFONT_NONSYMBOLIC);
57 ASSERT_TRUE(font_desc->KeyExist("FontBBox"));
58 EXPECT_EQ(4U, font_desc->GetArrayFor("FontBBox")->GetCount());
59 EXPECT_TRUE(font_desc->KeyExist("ItalicAngle"));
60 EXPECT_TRUE(font_desc->KeyExist("Ascent"));
61 EXPECT_TRUE(font_desc->KeyExist("Descent"));
62 EXPECT_TRUE(font_desc->KeyExist("CapHeight"));
63 EXPECT_TRUE(font_desc->KeyExist("StemV"));
64 CFX_ByteString present("FontFile");
65 CFX_ByteString absent("FontFile2");
66 if (font_type == FPDF_FONT_TRUETYPE)
67 std::swap(present, absent);
68 EXPECT_TRUE(font_desc->KeyExist(present));
69 EXPECT_FALSE(font_desc->KeyExist(absent));
70
71 // Check that the font stream is the one that was provided
72 CPDF_Stream* font_stream = font_desc->GetStreamFor(present);
73 ASSERT_EQ(size, font_stream->GetRawSize());
74 uint8_t* stream_data = font_stream->GetRawData();
75 for (size_t j = 0; j < size; j++)
76 EXPECT_EQ(data[j], stream_data[j]) << " at byte " << j;
77 }
78
79 void CheckCompositeFontWidths(CPDF_Array* widths_array,
80 CPDF_Font* typed_font) {
81 // Check that W array is in a format that conforms to PDF spec 1.7 section
82 // "Glyph Metrics in CIDFonts" (these checks are not
83 // implementation-specific).
84 EXPECT_GT(widths_array->GetCount(), 1U);
85 int num_cids_checked = 0;
86 int cur_cid = 0;
87 for (size_t idx = 0; idx < widths_array->GetCount(); idx++) {
88 int cid = widths_array->GetNumberAt(idx);
89 EXPECT_GE(cid, cur_cid);
90 ASSERT_FALSE(++idx == widths_array->GetCount());
91 CPDF_Object* next = widths_array->GetObjectAt(idx);
92 if (next->IsArray()) {
93 // We are in the c [w1 w2 ...] case
94 CPDF_Array* arr = next->AsArray();
95 int cnt = static_cast<int>(arr->GetCount());
96 size_t inner_idx = 0;
97 for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) {
98 int width = arr->GetNumberAt(inner_idx++);
99 EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid "
100 << cur_cid;
101 }
102 num_cids_checked += cnt;
103 continue;
104 }
105 // Otherwise, are in the c_first c_last w case.
106 ASSERT_TRUE(next->IsNumber());
107 int last_cid = next->AsNumber()->GetInteger();
108 ASSERT_FALSE(++idx == widths_array->GetCount());
109 int width = widths_array->GetNumberAt(idx);
110 for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) {
111 EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid)) << " at cid "
112 << cur_cid;
113 }
114 num_cids_checked += last_cid - cid + 1;
115 }
116 // Make sure we have a good amount of cids described
117 EXPECT_GT(num_cids_checked, 900);
118 }
119 CPDF_Document* cpdf_doc() { return cpdf_doc_; }
120
121 private:
122 CPDF_Document* cpdf_doc_;
123};
Tom Sepezd483eb42016-01-06 10:03:59 -0800124
etienneb7712c262016-04-26 08:13:45 -0700125namespace {
thestigdc7ec032016-11-21 15:32:52 -0800126
etienneb7712c262016-04-26 08:13:45 -0700127const char kExpectedPDF[] =
128 "%PDF-1.7\r\n"
129 "%\xA1\xB3\xC5\xD7\r\n"
130 "1 0 obj\r\n"
131 "<</Pages 2 0 R /Type/Catalog>>\r\n"
132 "endobj\r\n"
133 "2 0 obj\r\n"
134 "<</Count 1/Kids\\[ 4 0 R \\]/Type/Pages>>\r\n"
135 "endobj\r\n"
136 "3 0 obj\r\n"
137 "<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
138 "endobj\r\n"
139 "4 0 obj\r\n"
140 "<</Contents 5 0 R /MediaBox\\[ 0 0 640 480\\]"
141 "/Parent 2 0 R /Resources<<>>/Rotate 0/Type/Page"
142 ">>\r\n"
143 "endobj\r\n"
144 "5 0 obj\r\n"
145 "<</Filter/FlateDecode/Length 8>>stream\r\n"
146 // Character '_' is matching '\0' (see comment below).
147 "x\x9C\x3____\x1\r\n"
148 "endstream\r\n"
149 "endobj\r\n"
150 "xref\r\n"
151 "0 6\r\n"
152 "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"
157 "0000000301 00000 n\r\n"
158 "trailer\r\n"
159 "<<\r\n"
160 "/Root 1 0 R\r\n"
161 "/Info 3 0 R\r\n"
162 "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
163 "startxref\r\n"
164 "379\r\n"
165 "%%EOF\r\n";
thestigdc7ec032016-11-21 15:32:52 -0800166
167int GetBlockFromString(void* param,
168 unsigned long pos,
169 unsigned char* buf,
170 unsigned long size) {
171 std::string* new_file = static_cast<std::string*>(param);
172 if (!new_file || pos + size < pos)
173 return 0;
174
175 unsigned long file_size = new_file->size();
176 if (pos + size > file_size)
177 return 0;
178
179 memcpy(buf, new_file->data() + pos, size);
180 return 1;
181}
182
etienneb7712c262016-04-26 08:13:45 -0700183} // namespace
184
Tom Sepezd483eb42016-01-06 10:03:59 -0800185TEST_F(FPDFEditEmbeddertest, EmptyCreation) {
186 EXPECT_TRUE(CreateEmptyDocument());
weili9b777de2016-08-19 16:19:46 -0700187 FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
Tom Sepezd483eb42016-01-06 10:03:59 -0800188 EXPECT_NE(nullptr, page);
189 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Tom Sepez0aec19b2016-01-07 12:22:44 -0800190 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
etienneb7712c262016-04-26 08:13:45 -0700191
192 // The MatchesRegexp doesn't support embedded NUL ('\0') characters. They are
193 // replaced by '_' for the purpose of the test.
194 std::string result = GetString();
195 std::replace(result.begin(), result.end(), '\0', '_');
196 EXPECT_THAT(result, testing::MatchesRegex(
197 std::string(kExpectedPDF, sizeof(kExpectedPDF))));
weili9b777de2016-08-19 16:19:46 -0700198 FPDF_ClosePage(page);
Tom Sepezd483eb42016-01-06 10:03:59 -0800199}
thestigdc7ec032016-11-21 15:32:52 -0800200
201// Regression test for https://crbug.com/667012
202TEST_F(FPDFEditEmbeddertest, RasterizePDF) {
203 const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
204
205 // Get the bitmap for the original document/
206 FPDF_BITMAP orig_bitmap;
207 {
208 EXPECT_TRUE(OpenDocument("black.pdf"));
209 FPDF_PAGE orig_page = LoadPage(0);
210 EXPECT_NE(nullptr, orig_page);
211 orig_bitmap = RenderPage(orig_page);
212 CompareBitmap(orig_bitmap, 612, 792, kAllBlackMd5sum);
213 UnloadPage(orig_page);
214 }
215
216 // Create a new document from |orig_bitmap| and save it.
217 {
218 FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
219 FPDF_PAGE temp_page = FPDFPage_New(temp_doc, 0, 612, 792);
220
221 // Add the bitmap to an image object and add the image object to the output
222 // page.
Lei Zhangcbd89572017-03-15 17:35:47 -0700223 FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);
thestigdc7ec032016-11-21 15:32:52 -0800224 EXPECT_TRUE(FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap));
225 EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
226 FPDFPage_InsertObject(temp_page, temp_img);
227 EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
228 EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
229 FPDF_ClosePage(temp_page);
230 FPDF_CloseDocument(temp_doc);
231 }
232 FPDFBitmap_Destroy(orig_bitmap);
233
234 // Get the generated content. Make sure it is at least as big as the original
235 // PDF.
236 std::string new_file = GetString();
237 EXPECT_GT(new_file.size(), 923U);
238
239 // Read |new_file| in, and verify its rendered bitmap.
240 {
241 FPDF_FILEACCESS file_access;
242 memset(&file_access, 0, sizeof(file_access));
243 file_access.m_FileLen = new_file.size();
244 file_access.m_GetBlock = GetBlockFromString;
245 file_access.m_Param = &new_file;
246
247 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
248 EXPECT_EQ(1, FPDF_GetPageCount(document_));
249 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
250 EXPECT_NE(nullptr, new_page);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500251 FPDF_BITMAP new_bitmap = RenderPage(new_page);
thestigdc7ec032016-11-21 15:32:52 -0800252 CompareBitmap(new_bitmap, 612, 792, kAllBlackMd5sum);
253 FPDF_ClosePage(new_page);
254 FPDF_CloseDocument(new_doc);
255 FPDFBitmap_Destroy(new_bitmap);
256 }
257}
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500258
259TEST_F(FPDFEditEmbeddertest, AddPaths) {
260 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500261 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500262
263 // We will first add a red rectangle
264 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
265 ASSERT_NE(nullptr, red_rect);
266 // Expect false when trying to set colors out of range
267 EXPECT_FALSE(FPDFPath_SetStrokeColor(red_rect, 100, 100, 100, 300));
268 EXPECT_FALSE(FPDFPath_SetFillColor(red_rect, 200, 256, 200, 0));
269
270 // Fill rectangle with red and insert to the page
271 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
272 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
273 FPDFPage_InsertObject(page, red_rect);
274 EXPECT_TRUE(FPDFPage_GenerateContent(page));
275 FPDF_BITMAP page_bitmap = RenderPage(page);
276 CompareBitmap(page_bitmap, 612, 792, "66d02eaa6181e2c069ce2ea99beda497");
277 FPDFBitmap_Destroy(page_bitmap);
278
279 // Now add to that a green rectangle with some medium alpha
280 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
281 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 128));
282 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
283 FPDFPage_InsertObject(page, green_rect);
284 EXPECT_TRUE(FPDFPage_GenerateContent(page));
285 page_bitmap = RenderPage(page);
286 CompareBitmap(page_bitmap, 612, 792, "7b0b87604594e773add528fae567a558");
287 FPDFBitmap_Destroy(page_bitmap);
288
289 // Add a black triangle.
290 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
291 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 200));
292 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
293 EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
294 EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
295 EXPECT_TRUE(FPDFPath_Close(black_path));
296 FPDFPage_InsertObject(page, black_path);
297 EXPECT_TRUE(FPDFPage_GenerateContent(page));
298 page_bitmap = RenderPage(page);
299 CompareBitmap(page_bitmap, 612, 792, "eadc8020a14dfcf091da2688733d8806");
300 FPDFBitmap_Destroy(page_bitmap);
301
302 // Now add a more complex blue path.
303 FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
304 EXPECT_TRUE(FPDFPath_SetFillColor(blue_path, 0, 0, 255, 255));
305 EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
306 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
307 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
308 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
309 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
310 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
311 EXPECT_TRUE(FPDFPath_Close(blue_path));
312 FPDFPage_InsertObject(page, blue_path);
313 EXPECT_TRUE(FPDFPage_GenerateContent(page));
314 page_bitmap = RenderPage(page);
315 CompareBitmap(page_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
316 FPDFBitmap_Destroy(page_bitmap);
317
318 // Now save the result, closing the page and document
Nicolas Penad03ca422017-03-06 13:54:33 -0500319 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500320 FPDF_ClosePage(page);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500321 std::string new_file = GetString();
322
323 // Render the saved result
324 FPDF_FILEACCESS file_access;
325 memset(&file_access, 0, sizeof(file_access));
326 file_access.m_FileLen = new_file.size();
327 file_access.m_GetBlock = GetBlockFromString;
328 file_access.m_Param = &new_file;
329 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
330 ASSERT_NE(nullptr, new_doc);
331 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
332 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
333 ASSERT_NE(nullptr, new_page);
334 FPDF_BITMAP new_bitmap = RenderPage(new_page);
335 CompareBitmap(new_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
336 FPDFBitmap_Destroy(new_bitmap);
337 FPDF_ClosePage(new_page);
338 FPDF_CloseDocument(new_doc);
339}
340
341TEST_F(FPDFEditEmbeddertest, PathOnTopOfText) {
342 // Load document with some text
343 EXPECT_TRUE(OpenDocument("hello_world.pdf"));
344 FPDF_PAGE page = LoadPage(0);
345 EXPECT_NE(nullptr, page);
346
347 // Add an opaque rectangle on top of some of the text.
348 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
349 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
350 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
351 FPDFPage_InsertObject(page, red_rect);
352 EXPECT_TRUE(FPDFPage_GenerateContent(page));
353
354 // Add a transparent triangle on top of other part of the text.
355 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
356 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 100));
357 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
358 EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
359 EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
360 EXPECT_TRUE(FPDFPath_Close(black_path));
361 FPDFPage_InsertObject(page, black_path);
362 EXPECT_TRUE(FPDFPage_GenerateContent(page));
363
364 // Render and check the result. Text is slightly different on Mac.
365 FPDF_BITMAP bitmap = RenderPage(page);
366#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700367 const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500368#else
Lei Zhang3de50052017-03-29 21:02:13 -0700369 const char md5[] = "2fdfc5dda29374cfba4349362e38ebdb";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400370#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700371 CompareBitmap(bitmap, 200, 200, md5);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500372 FPDFBitmap_Destroy(bitmap);
373 UnloadPage(page);
374}
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500375
376TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) {
377 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500378 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500379
380 // Add a large stroked rectangle (fill color should not affect it).
381 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
382 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 255));
383 EXPECT_TRUE(FPDFPath_SetStrokeColor(rect, 0, 255, 0, 255));
384 EXPECT_TRUE(FPDFPath_SetStrokeWidth(rect, 15.0f));
385 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
386 FPDFPage_InsertObject(page, rect);
387 EXPECT_TRUE(FPDFPage_GenerateContent(page));
388 FPDF_BITMAP page_bitmap = RenderPage(page);
389 CompareBitmap(page_bitmap, 612, 792, "64bd31f862a89e0a9e505a5af6efd506");
390 FPDFBitmap_Destroy(page_bitmap);
391
392 // Add crossed-checkmark
393 FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
394 EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
395 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
396 EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
397 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
398 EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 128, 128, 128, 180));
399 EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
400 EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
401 FPDFPage_InsertObject(page, check);
402 EXPECT_TRUE(FPDFPage_GenerateContent(page));
403 page_bitmap = RenderPage(page);
404 CompareBitmap(page_bitmap, 612, 792, "4b6f3b9d25c4e194821217d5016c3724");
405 FPDFBitmap_Destroy(page_bitmap);
406
407 // Add stroked and filled oval-ish path.
408 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
409 EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
410 EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
411 EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
412 EXPECT_TRUE(FPDFPath_Close(path));
413 EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 128, 128, 100));
414 EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 128, 200, 128, 150));
415 EXPECT_TRUE(FPDFPath_SetStrokeWidth(path, 10.5f));
416 EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
417 FPDFPage_InsertObject(page, path);
418 EXPECT_TRUE(FPDFPage_GenerateContent(page));
419 page_bitmap = RenderPage(page);
420 CompareBitmap(page_bitmap, 612, 792, "ff3e6a22326754944cc6e56609acd73b");
421 FPDFBitmap_Destroy(page_bitmap);
422 FPDF_ClosePage(page);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500423}
Nicolas Pena49058402017-02-14 18:26:20 -0500424
425TEST_F(FPDFEditEmbeddertest, AddStandardFontText) {
426 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500427 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena49058402017-02-14 18:26:20 -0500428
429 // Add some text to the page
Nicolas Penad03ca422017-03-06 13:54:33 -0500430 FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500431 EXPECT_TRUE(text1);
432 EXPECT_TRUE(FPDFText_SetText(text1, "I'm at the bottom of the page"));
433 FPDFPageObj_Transform(text1, 1, 0, 0, 1, 20, 20);
434 FPDFPage_InsertObject(page, text1);
435 EXPECT_TRUE(FPDFPage_GenerateContent(page));
436 FPDF_BITMAP page_bitmap = RenderPage(page);
437#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700438 const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
Nicolas Pena49058402017-02-14 18:26:20 -0500439#else
Lei Zhang3de50052017-03-29 21:02:13 -0700440 const char md5[] = "6e8a9b0682f60fd3ff1bf087b093d30d";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400441#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700442 CompareBitmap(page_bitmap, 612, 792, md5);
Nicolas Pena49058402017-02-14 18:26:20 -0500443 FPDFBitmap_Destroy(page_bitmap);
444
445 // Try another font
446 FPDF_PAGEOBJECT text2 =
Nicolas Penad03ca422017-03-06 13:54:33 -0500447 FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500448 EXPECT_TRUE(text2);
449 EXPECT_TRUE(FPDFText_SetText(text2, "Hi, I'm Bold. Times New Roman Bold."));
450 FPDFPageObj_Transform(text2, 1, 0, 0, 1, 100, 600);
451 FPDFPage_InsertObject(page, text2);
452 EXPECT_TRUE(FPDFPage_GenerateContent(page));
453 page_bitmap = RenderPage(page);
454#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700455 const char md5_2[] = "a5c4ace4c6f27644094813fe1441a21c";
Lei Zhang3de50052017-03-29 21:02:13 -0700456#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
457 const char md5_2[] = "56863642d4d8b418cfd810fdb5a5d92f";
Nicolas Pena49058402017-02-14 18:26:20 -0500458#else
Lei Zhang3de50052017-03-29 21:02:13 -0700459 const char md5_2[] = "0c83875429688bda45a55a692d5aa781";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400460#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700461 CompareBitmap(page_bitmap, 612, 792, md5_2);
Nicolas Pena49058402017-02-14 18:26:20 -0500462 FPDFBitmap_Destroy(page_bitmap);
463
464 // And some randomly transformed text
Nicolas Penad03ca422017-03-06 13:54:33 -0500465 FPDF_PAGEOBJECT text3 =
466 FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500467 EXPECT_TRUE(text3);
468 EXPECT_TRUE(FPDFText_SetText(text3, "Can you read me? <:)>"));
469 FPDFPageObj_Transform(text3, 1, 1.5, 2, 0.5, 200, 200);
470 FPDFPage_InsertObject(page, text3);
471 EXPECT_TRUE(FPDFPage_GenerateContent(page));
472 page_bitmap = RenderPage(page);
473#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700474 const char md5_3[] = "40b3ef04f915ff4c4208948001763544";
Lei Zhang3de50052017-03-29 21:02:13 -0700475#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
476 const char md5_3[] = "174f30c36c73adb265c0f6ddb3a61c87";
Nicolas Pena49058402017-02-14 18:26:20 -0500477#else
Lei Zhang3de50052017-03-29 21:02:13 -0700478 const char md5_3[] = "abfa9246d60f821b5b7638efd32dfd92";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400479#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700480 CompareBitmap(page_bitmap, 612, 792, md5_3);
Nicolas Pena49058402017-02-14 18:26:20 -0500481 FPDFBitmap_Destroy(page_bitmap);
482
483 // TODO(npm): Why are there issues with text rotated by 90 degrees?
484 // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
485 FPDF_ClosePage(page);
Nicolas Pena49058402017-02-14 18:26:20 -0500486}
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500487
488TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
489 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500490 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500491
492 // Add a red rectangle with some non-default alpha
493 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
494 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 128));
495 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
496 FPDFPage_InsertObject(page, rect);
497 EXPECT_TRUE(FPDFPage_GenerateContent(page));
498
499 // Check the ExtGState
500 CPDF_Page* the_page = CPDFPageFromFPDFPage(page);
501 CPDF_Dictionary* graphics_dict =
502 the_page->m_pResources->GetDictFor("ExtGState");
503 ASSERT_TRUE(graphics_dict);
504 EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
505
506 // Check the bitmap
507 FPDF_BITMAP page_bitmap = RenderPage(page);
508 CompareBitmap(page_bitmap, 612, 792, "5384da3406d62360ffb5cac4476fff1c");
509 FPDFBitmap_Destroy(page_bitmap);
510
511 // Never mind, my new favorite color is blue, increase alpha
512 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 0, 0, 255, 180));
513 EXPECT_TRUE(FPDFPage_GenerateContent(page));
514 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
515
516 // Check that bitmap displays changed content
517 page_bitmap = RenderPage(page);
518 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
519 FPDFBitmap_Destroy(page_bitmap);
520
521 // And now generate, without changes
522 EXPECT_TRUE(FPDFPage_GenerateContent(page));
523 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
524 page_bitmap = RenderPage(page);
525 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
526 FPDFBitmap_Destroy(page_bitmap);
527
528 // Add some text to the page
Nicolas Penad03ca422017-03-06 13:54:33 -0500529 FPDF_PAGEOBJECT text = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500530 EXPECT_TRUE(FPDFText_SetText(text, "Something something #text# something"));
531 FPDFPageObj_Transform(text, 1, 0, 0, 1, 300, 300);
532 FPDFPage_InsertObject(page, text);
533 EXPECT_TRUE(FPDFPage_GenerateContent(page));
534 CPDF_Dictionary* font_dict = the_page->m_pResources->GetDictFor("Font");
535 ASSERT_TRUE(font_dict);
536 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
537
538 // Generate yet again, check dicts are reasonably sized
539 EXPECT_TRUE(FPDFPage_GenerateContent(page));
540 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
541 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
542 FPDF_ClosePage(page);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500543}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500544
Nicolas Penad03ca422017-03-06 13:54:33 -0500545TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) {
546 CreateNewDocument();
547 // TODO(npm): use other fonts after disallowing loading any font as any type
548 const CPDF_Font* stock_font =
549 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
550 const uint8_t* data = stock_font->m_Font.GetFontData();
551 const uint32_t size = stock_font->m_Font.GetSize();
552 FPDF_FONT font =
553 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500554 ASSERT_TRUE(font);
Nicolas Penad03ca422017-03-06 13:54:33 -0500555 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
556 EXPECT_TRUE(typed_font->IsType1Font());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500557
Nicolas Penad03ca422017-03-06 13:54:33 -0500558 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
Nicolas Penabe90aae2017-02-27 10:41:41 -0500559 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
560 EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
561 EXPECT_EQ("Times New Roman Bold", font_dict->GetStringFor("BaseFont"));
562 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
563 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
564 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
Nicolas Penad03ca422017-03-06 13:54:33 -0500565 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
566
Nicolas Penabe90aae2017-02-27 10:41:41 -0500567 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
Nicolas Penad03ca422017-03-06 13:54:33 -0500568 ASSERT_TRUE(widths_array);
569 ASSERT_EQ(224U, widths_array->GetCount());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500570 EXPECT_EQ(250, widths_array->GetNumberAt(0));
Nicolas Penad03ca422017-03-06 13:54:33 -0500571 EXPECT_EQ(569, widths_array->GetNumberAt(11));
572 EXPECT_EQ(500, widths_array->GetNumberAt(223));
573 CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, size, data);
574}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500575
Nicolas Penad03ca422017-03-06 13:54:33 -0500576TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) {
577 CreateNewDocument();
578 const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
579 const uint8_t* data = stock_font->m_Font.GetFontData();
580 const uint32_t size = stock_font->m_Font.GetSize();
581 FPDF_FONT font =
582 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false);
583 ASSERT_TRUE(font);
584 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
585 EXPECT_TRUE(typed_font->IsTrueTypeFont());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500586
Nicolas Penad03ca422017-03-06 13:54:33 -0500587 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
588 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
589 EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
590 EXPECT_EQ("Courier New", font_dict->GetStringFor("BaseFont"));
591 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
592 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
593 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
594 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
Nicolas Penabe90aae2017-02-27 10:41:41 -0500595
Nicolas Penad03ca422017-03-06 13:54:33 -0500596 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
597 ASSERT_TRUE(widths_array);
598 ASSERT_EQ(224U, widths_array->GetCount());
599 EXPECT_EQ(600, widths_array->GetNumberAt(33));
600 EXPECT_EQ(600, widths_array->GetNumberAt(74));
601 EXPECT_EQ(600, widths_array->GetNumberAt(223));
602 CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, size, data);
603}
604
605TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) {
606 CreateNewDocument();
607 const CPDF_Font* stock_font =
608 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
609 const uint8_t* data = stock_font->m_Font.GetFontData();
610 const uint32_t size = stock_font->m_Font.GetSize();
611 FPDF_FONT font =
612 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1);
613 ASSERT_TRUE(font);
614 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
615 EXPECT_TRUE(typed_font->IsCIDFont());
616
617 // Check font dictionary entries
618 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
619 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
620 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
621 EXPECT_EQ("Times New Roman-Identity-H", font_dict->GetStringFor("BaseFont"));
622 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
623 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
624 ASSERT_TRUE(descendant_array);
625 EXPECT_EQ(1U, descendant_array->GetCount());
626
627 // Check the CIDFontDict
628 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
629 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
630 EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
631 EXPECT_EQ("Times New Roman", cidfont_dict->GetStringFor("BaseFont"));
632 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
633 ASSERT_TRUE(cidinfo_dict);
634 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
635 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
636 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
637 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, size, data);
638
639 // Check widths
640 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
641 ASSERT_TRUE(widths_array);
642 // Note: widths can be described in different ways in the widths array. The
643 // following checks are specific to our current implementation.
644 EXPECT_EQ(32, widths_array->GetNumberAt(0));
645 CPDF_Array* arr = widths_array->GetArrayAt(1);
646 ASSERT_TRUE(arr);
647 // This font support chars 32 to 126
648 EXPECT_EQ(95U, arr->GetCount());
649 EXPECT_EQ(250, arr->GetNumberAt(0));
650 EXPECT_EQ(610, arr->GetNumberAt(44));
651 EXPECT_EQ(541, arr->GetNumberAt(94));
652 // Next range: 160 - 383
653 EXPECT_EQ(160, widths_array->GetNumberAt(2));
654 arr = widths_array->GetArrayAt(3);
655 ASSERT_TRUE(arr);
656
657 CheckCompositeFontWidths(widths_array, typed_font);
658}
659
660TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) {
661 CreateNewDocument();
662 const CPDF_Font* stock_font =
663 CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
664 const uint8_t* data = stock_font->m_Font.GetFontData();
665 const uint32_t size = stock_font->m_Font.GetSize();
666
667 FPDF_FONT font =
668 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1);
669 ASSERT_TRUE(font);
670 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
671 EXPECT_TRUE(typed_font->IsCIDFont());
672
673 // Check font dictionary entries
674 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
675 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
676 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
677 EXPECT_EQ("Arial Italic", font_dict->GetStringFor("BaseFont"));
678 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
679 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
680 ASSERT_TRUE(descendant_array);
681 EXPECT_EQ(1U, descendant_array->GetCount());
682
683 // Check the CIDFontDict
684 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
685 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
686 EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
687 EXPECT_EQ("Arial Italic", cidfont_dict->GetStringFor("BaseFont"));
688 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
689 ASSERT_TRUE(cidinfo_dict);
690 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
691 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
692 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
693 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, size,
694 data);
695
696 // Check widths
697 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
698 ASSERT_TRUE(widths_array);
699 CheckCompositeFontWidths(widths_array, typed_font);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500700}