blob: 19bdaf57ee5797adfd3f87434be8430f193efdb4 [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_
367 const char md5[] = "2f7c0deee10a9490538e195af64beb67";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400368 const char new_md5[] = "f9e6fa74230f234286bfcada9f7606d8";
369 CompareBitmap2Options(bitmap, 200, 200, md5, new_md5);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500370#else
371 const char md5[] = "17c942c76ff229200f2c98073bb60d85";
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500372 CompareBitmap(bitmap, 200, 200, md5);
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400373#endif
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500374 FPDFBitmap_Destroy(bitmap);
375 UnloadPage(page);
376}
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500377
378TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) {
379 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500380 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500381
382 // Add a large stroked rectangle (fill color should not affect it).
383 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
384 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 255));
385 EXPECT_TRUE(FPDFPath_SetStrokeColor(rect, 0, 255, 0, 255));
386 EXPECT_TRUE(FPDFPath_SetStrokeWidth(rect, 15.0f));
387 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
388 FPDFPage_InsertObject(page, rect);
389 EXPECT_TRUE(FPDFPage_GenerateContent(page));
390 FPDF_BITMAP page_bitmap = RenderPage(page);
391 CompareBitmap(page_bitmap, 612, 792, "64bd31f862a89e0a9e505a5af6efd506");
392 FPDFBitmap_Destroy(page_bitmap);
393
394 // Add crossed-checkmark
395 FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
396 EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
397 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
398 EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
399 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
400 EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 128, 128, 128, 180));
401 EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
402 EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
403 FPDFPage_InsertObject(page, check);
404 EXPECT_TRUE(FPDFPage_GenerateContent(page));
405 page_bitmap = RenderPage(page);
406 CompareBitmap(page_bitmap, 612, 792, "4b6f3b9d25c4e194821217d5016c3724");
407 FPDFBitmap_Destroy(page_bitmap);
408
409 // Add stroked and filled oval-ish path.
410 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
411 EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
412 EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
413 EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
414 EXPECT_TRUE(FPDFPath_Close(path));
415 EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 128, 128, 100));
416 EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 128, 200, 128, 150));
417 EXPECT_TRUE(FPDFPath_SetStrokeWidth(path, 10.5f));
418 EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
419 FPDFPage_InsertObject(page, path);
420 EXPECT_TRUE(FPDFPage_GenerateContent(page));
421 page_bitmap = RenderPage(page);
422 CompareBitmap(page_bitmap, 612, 792, "ff3e6a22326754944cc6e56609acd73b");
423 FPDFBitmap_Destroy(page_bitmap);
424 FPDF_ClosePage(page);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500425}
Nicolas Pena49058402017-02-14 18:26:20 -0500426
427TEST_F(FPDFEditEmbeddertest, AddStandardFontText) {
428 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500429 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena49058402017-02-14 18:26:20 -0500430
431 // Add some text to the page
Nicolas Penad03ca422017-03-06 13:54:33 -0500432 FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500433 EXPECT_TRUE(text1);
434 EXPECT_TRUE(FPDFText_SetText(text1, "I'm at the bottom of the page"));
435 FPDFPageObj_Transform(text1, 1, 0, 0, 1, 20, 20);
436 FPDFPage_InsertObject(page, text1);
437 EXPECT_TRUE(FPDFPage_GenerateContent(page));
438 FPDF_BITMAP page_bitmap = RenderPage(page);
439#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
440 const char md5[] = "e19c90395d73cb9f37a6c3b0e8b18a9e";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400441 const char new_md5[] = "a4dddc1a3930fa694bbff9789dab4161";
442 CompareBitmap2Options(page_bitmap, 612, 792, md5, new_md5);
Nicolas Pena49058402017-02-14 18:26:20 -0500443#else
444 const char md5[] = "7c3a36ba7cec01688a16a14bfed9ecfc";
Nicolas Pena49058402017-02-14 18:26:20 -0500445 CompareBitmap(page_bitmap, 612, 792, md5);
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400446#endif
Nicolas Pena49058402017-02-14 18:26:20 -0500447 FPDFBitmap_Destroy(page_bitmap);
448
449 // Try another font
450 FPDF_PAGEOBJECT text2 =
Nicolas Penad03ca422017-03-06 13:54:33 -0500451 FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500452 EXPECT_TRUE(text2);
453 EXPECT_TRUE(FPDFText_SetText(text2, "Hi, I'm Bold. Times New Roman Bold."));
454 FPDFPageObj_Transform(text2, 1, 0, 0, 1, 100, 600);
455 FPDFPage_InsertObject(page, text2);
456 EXPECT_TRUE(FPDFPage_GenerateContent(page));
457 page_bitmap = RenderPage(page);
458#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
459 const char md5_2[] = "8e1c43dca6be68d364dbc283f5521041";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400460 const char new_md5_2[] = "a5c4ace4c6f27644094813fe1441a21c";
461 CompareBitmap2Options(page_bitmap, 612, 792, md5_2, new_md5_2);
Nicolas Pena49058402017-02-14 18:26:20 -0500462#else
463 const char md5_2[] = "e0e0873e3a2634a6394a431a51ce90ff";
Nicolas Pena49058402017-02-14 18:26:20 -0500464 CompareBitmap(page_bitmap, 612, 792, md5_2);
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400465#endif
Nicolas Pena49058402017-02-14 18:26:20 -0500466 FPDFBitmap_Destroy(page_bitmap);
467
468 // And some randomly transformed text
Nicolas Penad03ca422017-03-06 13:54:33 -0500469 FPDF_PAGEOBJECT text3 =
470 FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500471 EXPECT_TRUE(text3);
472 EXPECT_TRUE(FPDFText_SetText(text3, "Can you read me? <:)>"));
473 FPDFPageObj_Transform(text3, 1, 1.5, 2, 0.5, 200, 200);
474 FPDFPage_InsertObject(page, text3);
475 EXPECT_TRUE(FPDFPage_GenerateContent(page));
476 page_bitmap = RenderPage(page);
477#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
478 const char md5_3[] = "c6e5df448428793c7e4b0c820bd8c85e";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400479 const char new_md5_3[] = "40b3ef04f915ff4c4208948001763544";
480 CompareBitmap2Options(page_bitmap, 612, 792, md5_3, new_md5_3);
Nicolas Pena49058402017-02-14 18:26:20 -0500481#else
482 const char md5_3[] = "903ee10b6a9f0be51ecad0a1a0eeb171";
Nicolas Pena49058402017-02-14 18:26:20 -0500483 CompareBitmap(page_bitmap, 612, 792, md5_3);
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400484#endif
Nicolas Pena49058402017-02-14 18:26:20 -0500485 FPDFBitmap_Destroy(page_bitmap);
486
487 // TODO(npm): Why are there issues with text rotated by 90 degrees?
488 // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
489 FPDF_ClosePage(page);
Nicolas Pena49058402017-02-14 18:26:20 -0500490}
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500491
492TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
493 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500494 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500495
496 // Add a red rectangle with some non-default alpha
497 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
498 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 128));
499 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
500 FPDFPage_InsertObject(page, rect);
501 EXPECT_TRUE(FPDFPage_GenerateContent(page));
502
503 // Check the ExtGState
504 CPDF_Page* the_page = CPDFPageFromFPDFPage(page);
505 CPDF_Dictionary* graphics_dict =
506 the_page->m_pResources->GetDictFor("ExtGState");
507 ASSERT_TRUE(graphics_dict);
508 EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
509
510 // Check the bitmap
511 FPDF_BITMAP page_bitmap = RenderPage(page);
512 CompareBitmap(page_bitmap, 612, 792, "5384da3406d62360ffb5cac4476fff1c");
513 FPDFBitmap_Destroy(page_bitmap);
514
515 // Never mind, my new favorite color is blue, increase alpha
516 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 0, 0, 255, 180));
517 EXPECT_TRUE(FPDFPage_GenerateContent(page));
518 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
519
520 // Check that bitmap displays changed content
521 page_bitmap = RenderPage(page);
522 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
523 FPDFBitmap_Destroy(page_bitmap);
524
525 // And now generate, without changes
526 EXPECT_TRUE(FPDFPage_GenerateContent(page));
527 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
528 page_bitmap = RenderPage(page);
529 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
530 FPDFBitmap_Destroy(page_bitmap);
531
532 // Add some text to the page
Nicolas Penad03ca422017-03-06 13:54:33 -0500533 FPDF_PAGEOBJECT text = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500534 EXPECT_TRUE(FPDFText_SetText(text, "Something something #text# something"));
535 FPDFPageObj_Transform(text, 1, 0, 0, 1, 300, 300);
536 FPDFPage_InsertObject(page, text);
537 EXPECT_TRUE(FPDFPage_GenerateContent(page));
538 CPDF_Dictionary* font_dict = the_page->m_pResources->GetDictFor("Font");
539 ASSERT_TRUE(font_dict);
540 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
541
542 // Generate yet again, check dicts are reasonably sized
543 EXPECT_TRUE(FPDFPage_GenerateContent(page));
544 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
545 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
546 FPDF_ClosePage(page);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500547}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500548
Nicolas Penad03ca422017-03-06 13:54:33 -0500549TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) {
550 CreateNewDocument();
551 // TODO(npm): use other fonts after disallowing loading any font as any type
552 const CPDF_Font* stock_font =
553 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
554 const uint8_t* data = stock_font->m_Font.GetFontData();
555 const uint32_t size = stock_font->m_Font.GetSize();
556 FPDF_FONT font =
557 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500558 ASSERT_TRUE(font);
Nicolas Penad03ca422017-03-06 13:54:33 -0500559 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
560 EXPECT_TRUE(typed_font->IsType1Font());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500561
Nicolas Penad03ca422017-03-06 13:54:33 -0500562 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
Nicolas Penabe90aae2017-02-27 10:41:41 -0500563 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
564 EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
565 EXPECT_EQ("Times New Roman Bold", font_dict->GetStringFor("BaseFont"));
566 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
567 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
568 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
Nicolas Penad03ca422017-03-06 13:54:33 -0500569 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
570
Nicolas Penabe90aae2017-02-27 10:41:41 -0500571 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
Nicolas Penad03ca422017-03-06 13:54:33 -0500572 ASSERT_TRUE(widths_array);
573 ASSERT_EQ(224U, widths_array->GetCount());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500574 EXPECT_EQ(250, widths_array->GetNumberAt(0));
Nicolas Penad03ca422017-03-06 13:54:33 -0500575 EXPECT_EQ(569, widths_array->GetNumberAt(11));
576 EXPECT_EQ(500, widths_array->GetNumberAt(223));
577 CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, size, data);
578}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500579
Nicolas Penad03ca422017-03-06 13:54:33 -0500580TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) {
581 CreateNewDocument();
582 const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
583 const uint8_t* data = stock_font->m_Font.GetFontData();
584 const uint32_t size = stock_font->m_Font.GetSize();
585 FPDF_FONT font =
586 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false);
587 ASSERT_TRUE(font);
588 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
589 EXPECT_TRUE(typed_font->IsTrueTypeFont());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500590
Nicolas Penad03ca422017-03-06 13:54:33 -0500591 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
592 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
593 EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
594 EXPECT_EQ("Courier New", font_dict->GetStringFor("BaseFont"));
595 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
596 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
597 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
598 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
Nicolas Penabe90aae2017-02-27 10:41:41 -0500599
Nicolas Penad03ca422017-03-06 13:54:33 -0500600 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
601 ASSERT_TRUE(widths_array);
602 ASSERT_EQ(224U, widths_array->GetCount());
603 EXPECT_EQ(600, widths_array->GetNumberAt(33));
604 EXPECT_EQ(600, widths_array->GetNumberAt(74));
605 EXPECT_EQ(600, widths_array->GetNumberAt(223));
606 CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, size, data);
607}
608
609TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) {
610 CreateNewDocument();
611 const CPDF_Font* stock_font =
612 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
613 const uint8_t* data = stock_font->m_Font.GetFontData();
614 const uint32_t size = stock_font->m_Font.GetSize();
615 FPDF_FONT font =
616 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1);
617 ASSERT_TRUE(font);
618 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
619 EXPECT_TRUE(typed_font->IsCIDFont());
620
621 // Check font dictionary entries
622 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
623 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
624 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
625 EXPECT_EQ("Times New Roman-Identity-H", font_dict->GetStringFor("BaseFont"));
626 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
627 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
628 ASSERT_TRUE(descendant_array);
629 EXPECT_EQ(1U, descendant_array->GetCount());
630
631 // Check the CIDFontDict
632 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
633 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
634 EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
635 EXPECT_EQ("Times New Roman", cidfont_dict->GetStringFor("BaseFont"));
636 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
637 ASSERT_TRUE(cidinfo_dict);
638 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
639 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
640 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
641 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, size, data);
642
643 // Check widths
644 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
645 ASSERT_TRUE(widths_array);
646 // Note: widths can be described in different ways in the widths array. The
647 // following checks are specific to our current implementation.
648 EXPECT_EQ(32, widths_array->GetNumberAt(0));
649 CPDF_Array* arr = widths_array->GetArrayAt(1);
650 ASSERT_TRUE(arr);
651 // This font support chars 32 to 126
652 EXPECT_EQ(95U, arr->GetCount());
653 EXPECT_EQ(250, arr->GetNumberAt(0));
654 EXPECT_EQ(610, arr->GetNumberAt(44));
655 EXPECT_EQ(541, arr->GetNumberAt(94));
656 // Next range: 160 - 383
657 EXPECT_EQ(160, widths_array->GetNumberAt(2));
658 arr = widths_array->GetArrayAt(3);
659 ASSERT_TRUE(arr);
660
661 CheckCompositeFontWidths(widths_array, typed_font);
662}
663
664TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) {
665 CreateNewDocument();
666 const CPDF_Font* stock_font =
667 CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
668 const uint8_t* data = stock_font->m_Font.GetFontData();
669 const uint32_t size = stock_font->m_Font.GetSize();
670
671 FPDF_FONT font =
672 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1);
673 ASSERT_TRUE(font);
674 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
675 EXPECT_TRUE(typed_font->IsCIDFont());
676
677 // Check font dictionary entries
678 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
679 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
680 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
681 EXPECT_EQ("Arial Italic", font_dict->GetStringFor("BaseFont"));
682 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
683 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
684 ASSERT_TRUE(descendant_array);
685 EXPECT_EQ(1U, descendant_array->GetCount());
686
687 // Check the CIDFontDict
688 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
689 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
690 EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
691 EXPECT_EQ("Arial Italic", cidfont_dict->GetStringFor("BaseFont"));
692 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
693 ASSERT_TRUE(cidinfo_dict);
694 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
695 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
696 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
697 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, size,
698 data);
699
700 // Check widths
701 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
702 ASSERT_TRUE(widths_array);
703 CheckCompositeFontWidths(widths_array, typed_font);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500704}