blob: 6454c34df17afab8d6f695be3772efcb2d0b903f [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));
Miklos Vajnaed4705b2017-04-05 09:24:50 +0200282
283 // Make sure we get back the same color we set previously.
284 unsigned int R;
285 unsigned int G;
286 unsigned int B;
287 unsigned int A;
288 EXPECT_TRUE(FPDFPath_GetFillColor(green_rect, &R, &G, &B, &A));
289 EXPECT_EQ(0U, R);
290 EXPECT_EQ(255U, G);
291 EXPECT_EQ(0U, B);
292 EXPECT_EQ(128U, A);
293
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500294 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
295 FPDFPage_InsertObject(page, green_rect);
296 EXPECT_TRUE(FPDFPage_GenerateContent(page));
297 page_bitmap = RenderPage(page);
298 CompareBitmap(page_bitmap, 612, 792, "7b0b87604594e773add528fae567a558");
299 FPDFBitmap_Destroy(page_bitmap);
300
301 // Add a black triangle.
302 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
303 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 200));
304 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
305 EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
306 EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
307 EXPECT_TRUE(FPDFPath_Close(black_path));
308 FPDFPage_InsertObject(page, black_path);
309 EXPECT_TRUE(FPDFPage_GenerateContent(page));
310 page_bitmap = RenderPage(page);
311 CompareBitmap(page_bitmap, 612, 792, "eadc8020a14dfcf091da2688733d8806");
312 FPDFBitmap_Destroy(page_bitmap);
313
314 // Now add a more complex blue path.
315 FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
316 EXPECT_TRUE(FPDFPath_SetFillColor(blue_path, 0, 0, 255, 255));
317 EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
318 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
319 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
320 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
321 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
322 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
323 EXPECT_TRUE(FPDFPath_Close(blue_path));
324 FPDFPage_InsertObject(page, blue_path);
325 EXPECT_TRUE(FPDFPage_GenerateContent(page));
326 page_bitmap = RenderPage(page);
327 CompareBitmap(page_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
328 FPDFBitmap_Destroy(page_bitmap);
329
330 // Now save the result, closing the page and document
Nicolas Penad03ca422017-03-06 13:54:33 -0500331 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500332 FPDF_ClosePage(page);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500333 std::string new_file = GetString();
334
335 // Render the saved result
336 FPDF_FILEACCESS file_access;
337 memset(&file_access, 0, sizeof(file_access));
338 file_access.m_FileLen = new_file.size();
339 file_access.m_GetBlock = GetBlockFromString;
340 file_access.m_Param = &new_file;
341 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
342 ASSERT_NE(nullptr, new_doc);
343 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
344 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
345 ASSERT_NE(nullptr, new_page);
346 FPDF_BITMAP new_bitmap = RenderPage(new_page);
347 CompareBitmap(new_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
348 FPDFBitmap_Destroy(new_bitmap);
349 FPDF_ClosePage(new_page);
350 FPDF_CloseDocument(new_doc);
351}
352
353TEST_F(FPDFEditEmbeddertest, PathOnTopOfText) {
354 // Load document with some text
355 EXPECT_TRUE(OpenDocument("hello_world.pdf"));
356 FPDF_PAGE page = LoadPage(0);
357 EXPECT_NE(nullptr, page);
358
359 // Add an opaque rectangle on top of some of the text.
360 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
361 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
362 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
363 FPDFPage_InsertObject(page, red_rect);
364 EXPECT_TRUE(FPDFPage_GenerateContent(page));
365
366 // Add a transparent triangle on top of other part of the text.
367 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
368 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 100));
369 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
370 EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
371 EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
372 EXPECT_TRUE(FPDFPath_Close(black_path));
373 FPDFPage_InsertObject(page, black_path);
374 EXPECT_TRUE(FPDFPage_GenerateContent(page));
375
376 // Render and check the result. Text is slightly different on Mac.
377 FPDF_BITMAP bitmap = RenderPage(page);
378#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700379 const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500380#else
Lei Zhang3de50052017-03-29 21:02:13 -0700381 const char md5[] = "2fdfc5dda29374cfba4349362e38ebdb";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400382#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700383 CompareBitmap(bitmap, 200, 200, md5);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500384 FPDFBitmap_Destroy(bitmap);
385 UnloadPage(page);
386}
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500387
388TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) {
389 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500390 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500391
392 // Add a large stroked rectangle (fill color should not affect it).
393 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
394 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 255));
395 EXPECT_TRUE(FPDFPath_SetStrokeColor(rect, 0, 255, 0, 255));
396 EXPECT_TRUE(FPDFPath_SetStrokeWidth(rect, 15.0f));
397 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
398 FPDFPage_InsertObject(page, rect);
399 EXPECT_TRUE(FPDFPage_GenerateContent(page));
400 FPDF_BITMAP page_bitmap = RenderPage(page);
401 CompareBitmap(page_bitmap, 612, 792, "64bd31f862a89e0a9e505a5af6efd506");
402 FPDFBitmap_Destroy(page_bitmap);
403
404 // Add crossed-checkmark
405 FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
406 EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
407 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
408 EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
409 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
410 EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 128, 128, 128, 180));
411 EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
412 EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
413 FPDFPage_InsertObject(page, check);
414 EXPECT_TRUE(FPDFPage_GenerateContent(page));
415 page_bitmap = RenderPage(page);
416 CompareBitmap(page_bitmap, 612, 792, "4b6f3b9d25c4e194821217d5016c3724");
417 FPDFBitmap_Destroy(page_bitmap);
418
419 // Add stroked and filled oval-ish path.
420 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
421 EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
422 EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
423 EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
424 EXPECT_TRUE(FPDFPath_Close(path));
425 EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 128, 128, 100));
426 EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 128, 200, 128, 150));
427 EXPECT_TRUE(FPDFPath_SetStrokeWidth(path, 10.5f));
428 EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
429 FPDFPage_InsertObject(page, path);
430 EXPECT_TRUE(FPDFPage_GenerateContent(page));
431 page_bitmap = RenderPage(page);
432 CompareBitmap(page_bitmap, 612, 792, "ff3e6a22326754944cc6e56609acd73b");
433 FPDFBitmap_Destroy(page_bitmap);
434 FPDF_ClosePage(page);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500435}
Nicolas Pena49058402017-02-14 18:26:20 -0500436
437TEST_F(FPDFEditEmbeddertest, AddStandardFontText) {
438 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500439 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena49058402017-02-14 18:26:20 -0500440
441 // Add some text to the page
Nicolas Penad03ca422017-03-06 13:54:33 -0500442 FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500443 EXPECT_TRUE(text1);
444 EXPECT_TRUE(FPDFText_SetText(text1, "I'm at the bottom of the page"));
445 FPDFPageObj_Transform(text1, 1, 0, 0, 1, 20, 20);
446 FPDFPage_InsertObject(page, text1);
447 EXPECT_TRUE(FPDFPage_GenerateContent(page));
448 FPDF_BITMAP page_bitmap = RenderPage(page);
449#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700450 const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
Nicolas Pena49058402017-02-14 18:26:20 -0500451#else
Lei Zhang3de50052017-03-29 21:02:13 -0700452 const char md5[] = "6e8a9b0682f60fd3ff1bf087b093d30d";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400453#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700454 CompareBitmap(page_bitmap, 612, 792, md5);
Nicolas Pena49058402017-02-14 18:26:20 -0500455 FPDFBitmap_Destroy(page_bitmap);
456
457 // Try another font
458 FPDF_PAGEOBJECT text2 =
Nicolas Penad03ca422017-03-06 13:54:33 -0500459 FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500460 EXPECT_TRUE(text2);
461 EXPECT_TRUE(FPDFText_SetText(text2, "Hi, I'm Bold. Times New Roman Bold."));
462 FPDFPageObj_Transform(text2, 1, 0, 0, 1, 100, 600);
463 FPDFPage_InsertObject(page, text2);
464 EXPECT_TRUE(FPDFPage_GenerateContent(page));
465 page_bitmap = RenderPage(page);
466#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700467 const char md5_2[] = "a5c4ace4c6f27644094813fe1441a21c";
Lei Zhang3de50052017-03-29 21:02:13 -0700468#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
469 const char md5_2[] = "56863642d4d8b418cfd810fdb5a5d92f";
Nicolas Pena49058402017-02-14 18:26:20 -0500470#else
Lei Zhang3de50052017-03-29 21:02:13 -0700471 const char md5_2[] = "0c83875429688bda45a55a692d5aa781";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400472#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700473 CompareBitmap(page_bitmap, 612, 792, md5_2);
Nicolas Pena49058402017-02-14 18:26:20 -0500474 FPDFBitmap_Destroy(page_bitmap);
475
476 // And some randomly transformed text
Nicolas Penad03ca422017-03-06 13:54:33 -0500477 FPDF_PAGEOBJECT text3 =
478 FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500479 EXPECT_TRUE(text3);
480 EXPECT_TRUE(FPDFText_SetText(text3, "Can you read me? <:)>"));
481 FPDFPageObj_Transform(text3, 1, 1.5, 2, 0.5, 200, 200);
482 FPDFPage_InsertObject(page, text3);
483 EXPECT_TRUE(FPDFPage_GenerateContent(page));
484 page_bitmap = RenderPage(page);
485#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700486 const char md5_3[] = "40b3ef04f915ff4c4208948001763544";
Lei Zhang3de50052017-03-29 21:02:13 -0700487#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
488 const char md5_3[] = "174f30c36c73adb265c0f6ddb3a61c87";
Nicolas Pena49058402017-02-14 18:26:20 -0500489#else
Lei Zhang3de50052017-03-29 21:02:13 -0700490 const char md5_3[] = "abfa9246d60f821b5b7638efd32dfd92";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400491#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700492 CompareBitmap(page_bitmap, 612, 792, md5_3);
Nicolas Pena49058402017-02-14 18:26:20 -0500493 FPDFBitmap_Destroy(page_bitmap);
494
495 // TODO(npm): Why are there issues with text rotated by 90 degrees?
496 // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
497 FPDF_ClosePage(page);
Nicolas Pena49058402017-02-14 18:26:20 -0500498}
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500499
500TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
501 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500502 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500503
504 // Add a red rectangle with some non-default alpha
505 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
506 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 128));
507 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
508 FPDFPage_InsertObject(page, rect);
509 EXPECT_TRUE(FPDFPage_GenerateContent(page));
510
511 // Check the ExtGState
512 CPDF_Page* the_page = CPDFPageFromFPDFPage(page);
513 CPDF_Dictionary* graphics_dict =
514 the_page->m_pResources->GetDictFor("ExtGState");
515 ASSERT_TRUE(graphics_dict);
516 EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
517
518 // Check the bitmap
519 FPDF_BITMAP page_bitmap = RenderPage(page);
520 CompareBitmap(page_bitmap, 612, 792, "5384da3406d62360ffb5cac4476fff1c");
521 FPDFBitmap_Destroy(page_bitmap);
522
523 // Never mind, my new favorite color is blue, increase alpha
524 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 0, 0, 255, 180));
525 EXPECT_TRUE(FPDFPage_GenerateContent(page));
526 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
527
528 // Check that bitmap displays changed content
529 page_bitmap = RenderPage(page);
530 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
531 FPDFBitmap_Destroy(page_bitmap);
532
533 // And now generate, without changes
534 EXPECT_TRUE(FPDFPage_GenerateContent(page));
535 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
536 page_bitmap = RenderPage(page);
537 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
538 FPDFBitmap_Destroy(page_bitmap);
539
540 // Add some text to the page
Nicolas Penad03ca422017-03-06 13:54:33 -0500541 FPDF_PAGEOBJECT text = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500542 EXPECT_TRUE(FPDFText_SetText(text, "Something something #text# something"));
543 FPDFPageObj_Transform(text, 1, 0, 0, 1, 300, 300);
544 FPDFPage_InsertObject(page, text);
545 EXPECT_TRUE(FPDFPage_GenerateContent(page));
546 CPDF_Dictionary* font_dict = the_page->m_pResources->GetDictFor("Font");
547 ASSERT_TRUE(font_dict);
548 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
549
550 // Generate yet again, check dicts are reasonably sized
551 EXPECT_TRUE(FPDFPage_GenerateContent(page));
552 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
553 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
554 FPDF_ClosePage(page);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500555}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500556
Nicolas Penad03ca422017-03-06 13:54:33 -0500557TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) {
558 CreateNewDocument();
559 // TODO(npm): use other fonts after disallowing loading any font as any type
560 const CPDF_Font* stock_font =
561 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
562 const uint8_t* data = stock_font->m_Font.GetFontData();
563 const uint32_t size = stock_font->m_Font.GetSize();
564 FPDF_FONT font =
565 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500566 ASSERT_TRUE(font);
Nicolas Penad03ca422017-03-06 13:54:33 -0500567 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
568 EXPECT_TRUE(typed_font->IsType1Font());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500569
Nicolas Penad03ca422017-03-06 13:54:33 -0500570 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
Nicolas Penabe90aae2017-02-27 10:41:41 -0500571 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
572 EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
573 EXPECT_EQ("Times New Roman Bold", font_dict->GetStringFor("BaseFont"));
574 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
575 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
576 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
Nicolas Penad03ca422017-03-06 13:54:33 -0500577 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
578
Nicolas Penabe90aae2017-02-27 10:41:41 -0500579 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
Nicolas Penad03ca422017-03-06 13:54:33 -0500580 ASSERT_TRUE(widths_array);
581 ASSERT_EQ(224U, widths_array->GetCount());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500582 EXPECT_EQ(250, widths_array->GetNumberAt(0));
Nicolas Penad03ca422017-03-06 13:54:33 -0500583 EXPECT_EQ(569, widths_array->GetNumberAt(11));
584 EXPECT_EQ(500, widths_array->GetNumberAt(223));
585 CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, size, data);
586}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500587
Nicolas Penad03ca422017-03-06 13:54:33 -0500588TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) {
589 CreateNewDocument();
590 const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
591 const uint8_t* data = stock_font->m_Font.GetFontData();
592 const uint32_t size = stock_font->m_Font.GetSize();
593 FPDF_FONT font =
594 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false);
595 ASSERT_TRUE(font);
596 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
597 EXPECT_TRUE(typed_font->IsTrueTypeFont());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500598
Nicolas Penad03ca422017-03-06 13:54:33 -0500599 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
600 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
601 EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
602 EXPECT_EQ("Courier New", font_dict->GetStringFor("BaseFont"));
603 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
604 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
605 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
606 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
Nicolas Penabe90aae2017-02-27 10:41:41 -0500607
Nicolas Penad03ca422017-03-06 13:54:33 -0500608 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
609 ASSERT_TRUE(widths_array);
610 ASSERT_EQ(224U, widths_array->GetCount());
611 EXPECT_EQ(600, widths_array->GetNumberAt(33));
612 EXPECT_EQ(600, widths_array->GetNumberAt(74));
613 EXPECT_EQ(600, widths_array->GetNumberAt(223));
614 CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, size, data);
615}
616
617TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) {
618 CreateNewDocument();
619 const CPDF_Font* stock_font =
620 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
621 const uint8_t* data = stock_font->m_Font.GetFontData();
622 const uint32_t size = stock_font->m_Font.GetSize();
623 FPDF_FONT font =
624 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1);
625 ASSERT_TRUE(font);
626 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
627 EXPECT_TRUE(typed_font->IsCIDFont());
628
629 // Check font dictionary entries
630 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
631 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
632 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
633 EXPECT_EQ("Times New Roman-Identity-H", font_dict->GetStringFor("BaseFont"));
634 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
635 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
636 ASSERT_TRUE(descendant_array);
637 EXPECT_EQ(1U, descendant_array->GetCount());
638
639 // Check the CIDFontDict
640 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
641 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
642 EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
643 EXPECT_EQ("Times New Roman", cidfont_dict->GetStringFor("BaseFont"));
644 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
645 ASSERT_TRUE(cidinfo_dict);
646 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
647 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
648 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
649 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, size, data);
650
651 // Check widths
652 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
653 ASSERT_TRUE(widths_array);
654 // Note: widths can be described in different ways in the widths array. The
655 // following checks are specific to our current implementation.
656 EXPECT_EQ(32, widths_array->GetNumberAt(0));
657 CPDF_Array* arr = widths_array->GetArrayAt(1);
658 ASSERT_TRUE(arr);
659 // This font support chars 32 to 126
660 EXPECT_EQ(95U, arr->GetCount());
661 EXPECT_EQ(250, arr->GetNumberAt(0));
662 EXPECT_EQ(610, arr->GetNumberAt(44));
663 EXPECT_EQ(541, arr->GetNumberAt(94));
664 // Next range: 160 - 383
665 EXPECT_EQ(160, widths_array->GetNumberAt(2));
666 arr = widths_array->GetArrayAt(3);
667 ASSERT_TRUE(arr);
668
669 CheckCompositeFontWidths(widths_array, typed_font);
670}
671
672TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) {
673 CreateNewDocument();
674 const CPDF_Font* stock_font =
675 CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
676 const uint8_t* data = stock_font->m_Font.GetFontData();
677 const uint32_t size = stock_font->m_Font.GetSize();
678
679 FPDF_FONT font =
680 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1);
681 ASSERT_TRUE(font);
682 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
683 EXPECT_TRUE(typed_font->IsCIDFont());
684
685 // Check font dictionary entries
686 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
687 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
688 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
689 EXPECT_EQ("Arial Italic", font_dict->GetStringFor("BaseFont"));
690 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
691 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
692 ASSERT_TRUE(descendant_array);
693 EXPECT_EQ(1U, descendant_array->GetCount());
694
695 // Check the CIDFontDict
696 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
697 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
698 EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
699 EXPECT_EQ("Arial Italic", cidfont_dict->GetStringFor("BaseFont"));
700 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
701 ASSERT_TRUE(cidinfo_dict);
702 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
703 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
704 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
705 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, size,
706 data);
707
708 // Check widths
709 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
710 ASSERT_TRUE(widths_array);
711 CheckCompositeFontWidths(widths_array, typed_font);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500712}