blob: d62f5ca85c61188ea08fb8349b02032d341a6d81 [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
etienneb7712c262016-04-26 08:13:45 -0700167} // namespace
168
Tom Sepezd483eb42016-01-06 10:03:59 -0800169TEST_F(FPDFEditEmbeddertest, EmptyCreation) {
170 EXPECT_TRUE(CreateEmptyDocument());
weili9b777de2016-08-19 16:19:46 -0700171 FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
Tom Sepezd483eb42016-01-06 10:03:59 -0800172 EXPECT_NE(nullptr, page);
173 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Tom Sepez0aec19b2016-01-07 12:22:44 -0800174 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
etienneb7712c262016-04-26 08:13:45 -0700175
176 // The MatchesRegexp doesn't support embedded NUL ('\0') characters. They are
177 // replaced by '_' for the purpose of the test.
178 std::string result = GetString();
179 std::replace(result.begin(), result.end(), '\0', '_');
180 EXPECT_THAT(result, testing::MatchesRegex(
181 std::string(kExpectedPDF, sizeof(kExpectedPDF))));
weili9b777de2016-08-19 16:19:46 -0700182 FPDF_ClosePage(page);
Tom Sepezd483eb42016-01-06 10:03:59 -0800183}
thestigdc7ec032016-11-21 15:32:52 -0800184
185// Regression test for https://crbug.com/667012
186TEST_F(FPDFEditEmbeddertest, RasterizePDF) {
187 const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
188
189 // Get the bitmap for the original document/
190 FPDF_BITMAP orig_bitmap;
191 {
192 EXPECT_TRUE(OpenDocument("black.pdf"));
193 FPDF_PAGE orig_page = LoadPage(0);
194 EXPECT_NE(nullptr, orig_page);
195 orig_bitmap = RenderPage(orig_page);
196 CompareBitmap(orig_bitmap, 612, 792, kAllBlackMd5sum);
197 UnloadPage(orig_page);
198 }
199
200 // Create a new document from |orig_bitmap| and save it.
201 {
202 FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
203 FPDF_PAGE temp_page = FPDFPage_New(temp_doc, 0, 612, 792);
204
205 // Add the bitmap to an image object and add the image object to the output
206 // page.
Lei Zhangcbd89572017-03-15 17:35:47 -0700207 FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImageObj(temp_doc);
thestigdc7ec032016-11-21 15:32:52 -0800208 EXPECT_TRUE(FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap));
209 EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
210 FPDFPage_InsertObject(temp_page, temp_img);
211 EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
212 EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
213 FPDF_ClosePage(temp_page);
214 FPDF_CloseDocument(temp_doc);
215 }
216 FPDFBitmap_Destroy(orig_bitmap);
217
218 // Get the generated content. Make sure it is at least as big as the original
219 // PDF.
220 std::string new_file = GetString();
221 EXPECT_GT(new_file.size(), 923U);
222
223 // Read |new_file| in, and verify its rendered bitmap.
224 {
225 FPDF_FILEACCESS file_access;
226 memset(&file_access, 0, sizeof(file_access));
227 file_access.m_FileLen = new_file.size();
228 file_access.m_GetBlock = GetBlockFromString;
229 file_access.m_Param = &new_file;
230
231 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
232 EXPECT_EQ(1, FPDF_GetPageCount(document_));
233 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
234 EXPECT_NE(nullptr, new_page);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500235 FPDF_BITMAP new_bitmap = RenderPage(new_page);
thestigdc7ec032016-11-21 15:32:52 -0800236 CompareBitmap(new_bitmap, 612, 792, kAllBlackMd5sum);
237 FPDF_ClosePage(new_page);
238 FPDF_CloseDocument(new_doc);
239 FPDFBitmap_Destroy(new_bitmap);
240 }
241}
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500242
243TEST_F(FPDFEditEmbeddertest, AddPaths) {
244 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500245 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500246
247 // We will first add a red rectangle
248 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
249 ASSERT_NE(nullptr, red_rect);
250 // Expect false when trying to set colors out of range
251 EXPECT_FALSE(FPDFPath_SetStrokeColor(red_rect, 100, 100, 100, 300));
252 EXPECT_FALSE(FPDFPath_SetFillColor(red_rect, 200, 256, 200, 0));
253
254 // Fill rectangle with red and insert to the page
255 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
256 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
257 FPDFPage_InsertObject(page, red_rect);
258 EXPECT_TRUE(FPDFPage_GenerateContent(page));
259 FPDF_BITMAP page_bitmap = RenderPage(page);
260 CompareBitmap(page_bitmap, 612, 792, "66d02eaa6181e2c069ce2ea99beda497");
261 FPDFBitmap_Destroy(page_bitmap);
262
263 // Now add to that a green rectangle with some medium alpha
264 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
265 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 128));
Miklos Vajnaed4705b2017-04-05 09:24:50 +0200266
267 // Make sure we get back the same color we set previously.
268 unsigned int R;
269 unsigned int G;
270 unsigned int B;
271 unsigned int A;
272 EXPECT_TRUE(FPDFPath_GetFillColor(green_rect, &R, &G, &B, &A));
273 EXPECT_EQ(0U, R);
274 EXPECT_EQ(255U, G);
275 EXPECT_EQ(0U, B);
276 EXPECT_EQ(128U, A);
277
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500278 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
279 FPDFPage_InsertObject(page, green_rect);
280 EXPECT_TRUE(FPDFPage_GenerateContent(page));
281 page_bitmap = RenderPage(page);
282 CompareBitmap(page_bitmap, 612, 792, "7b0b87604594e773add528fae567a558");
283 FPDFBitmap_Destroy(page_bitmap);
284
285 // Add a black triangle.
286 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
287 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 200));
288 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
289 EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
290 EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
291 EXPECT_TRUE(FPDFPath_Close(black_path));
292 FPDFPage_InsertObject(page, black_path);
293 EXPECT_TRUE(FPDFPage_GenerateContent(page));
294 page_bitmap = RenderPage(page);
295 CompareBitmap(page_bitmap, 612, 792, "eadc8020a14dfcf091da2688733d8806");
296 FPDFBitmap_Destroy(page_bitmap);
297
298 // Now add a more complex blue path.
299 FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
300 EXPECT_TRUE(FPDFPath_SetFillColor(blue_path, 0, 0, 255, 255));
301 EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
302 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
303 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
304 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
305 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
306 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
307 EXPECT_TRUE(FPDFPath_Close(blue_path));
308 FPDFPage_InsertObject(page, blue_path);
309 EXPECT_TRUE(FPDFPage_GenerateContent(page));
310 page_bitmap = RenderPage(page);
311 CompareBitmap(page_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
312 FPDFBitmap_Destroy(page_bitmap);
313
314 // Now save the result, closing the page and document
Nicolas Penad03ca422017-03-06 13:54:33 -0500315 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500316 FPDF_ClosePage(page);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500317 std::string new_file = GetString();
318
319 // Render the saved result
320 FPDF_FILEACCESS file_access;
321 memset(&file_access, 0, sizeof(file_access));
322 file_access.m_FileLen = new_file.size();
323 file_access.m_GetBlock = GetBlockFromString;
324 file_access.m_Param = &new_file;
325 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
326 ASSERT_NE(nullptr, new_doc);
327 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
328 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
329 ASSERT_NE(nullptr, new_page);
330 FPDF_BITMAP new_bitmap = RenderPage(new_page);
331 CompareBitmap(new_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
332 FPDFBitmap_Destroy(new_bitmap);
333 FPDF_ClosePage(new_page);
334 FPDF_CloseDocument(new_doc);
335}
336
337TEST_F(FPDFEditEmbeddertest, PathOnTopOfText) {
338 // Load document with some text
339 EXPECT_TRUE(OpenDocument("hello_world.pdf"));
340 FPDF_PAGE page = LoadPage(0);
341 EXPECT_NE(nullptr, page);
342
343 // Add an opaque rectangle on top of some of the text.
344 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
345 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
346 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
347 FPDFPage_InsertObject(page, red_rect);
348 EXPECT_TRUE(FPDFPage_GenerateContent(page));
349
350 // Add a transparent triangle on top of other part of the text.
351 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
352 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 100));
353 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
354 EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
355 EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
356 EXPECT_TRUE(FPDFPath_Close(black_path));
357 FPDFPage_InsertObject(page, black_path);
358 EXPECT_TRUE(FPDFPage_GenerateContent(page));
359
360 // Render and check the result. Text is slightly different on Mac.
361 FPDF_BITMAP bitmap = RenderPage(page);
362#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700363 const char md5[] = "f9e6fa74230f234286bfcada9f7606d8";
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500364#else
Lei Zhang3de50052017-03-29 21:02:13 -0700365 const char md5[] = "2fdfc5dda29374cfba4349362e38ebdb";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400366#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700367 CompareBitmap(bitmap, 200, 200, md5);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500368 FPDFBitmap_Destroy(bitmap);
369 UnloadPage(page);
370}
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500371
372TEST_F(FPDFEditEmbeddertest, AddStrokedPaths) {
373 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500374 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500375
376 // Add a large stroked rectangle (fill color should not affect it).
377 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
378 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 255));
379 EXPECT_TRUE(FPDFPath_SetStrokeColor(rect, 0, 255, 0, 255));
380 EXPECT_TRUE(FPDFPath_SetStrokeWidth(rect, 15.0f));
381 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
382 FPDFPage_InsertObject(page, rect);
383 EXPECT_TRUE(FPDFPage_GenerateContent(page));
384 FPDF_BITMAP page_bitmap = RenderPage(page);
385 CompareBitmap(page_bitmap, 612, 792, "64bd31f862a89e0a9e505a5af6efd506");
386 FPDFBitmap_Destroy(page_bitmap);
387
388 // Add crossed-checkmark
389 FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
390 EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
391 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
392 EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
393 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
394 EXPECT_TRUE(FPDFPath_SetStrokeColor(check, 128, 128, 128, 180));
395 EXPECT_TRUE(FPDFPath_SetStrokeWidth(check, 8.35f));
396 EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
397 FPDFPage_InsertObject(page, check);
398 EXPECT_TRUE(FPDFPage_GenerateContent(page));
399 page_bitmap = RenderPage(page);
400 CompareBitmap(page_bitmap, 612, 792, "4b6f3b9d25c4e194821217d5016c3724");
401 FPDFBitmap_Destroy(page_bitmap);
402
403 // Add stroked and filled oval-ish path.
404 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
405 EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
406 EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
407 EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
408 EXPECT_TRUE(FPDFPath_Close(path));
409 EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 128, 128, 100));
410 EXPECT_TRUE(FPDFPath_SetStrokeColor(path, 128, 200, 128, 150));
411 EXPECT_TRUE(FPDFPath_SetStrokeWidth(path, 10.5f));
412 EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
413 FPDFPage_InsertObject(page, path);
414 EXPECT_TRUE(FPDFPage_GenerateContent(page));
415 page_bitmap = RenderPage(page);
416 CompareBitmap(page_bitmap, 612, 792, "ff3e6a22326754944cc6e56609acd73b");
417 FPDFBitmap_Destroy(page_bitmap);
418 FPDF_ClosePage(page);
Nicolas Pena2eb1a702017-02-09 18:17:33 -0500419}
Nicolas Pena49058402017-02-14 18:26:20 -0500420
421TEST_F(FPDFEditEmbeddertest, AddStandardFontText) {
422 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500423 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Pena49058402017-02-14 18:26:20 -0500424
425 // Add some text to the page
Nicolas Penad03ca422017-03-06 13:54:33 -0500426 FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500427 EXPECT_TRUE(text1);
428 EXPECT_TRUE(FPDFText_SetText(text1, "I'm at the bottom of the page"));
429 FPDFPageObj_Transform(text1, 1, 0, 0, 1, 20, 20);
430 FPDFPage_InsertObject(page, text1);
431 EXPECT_TRUE(FPDFPage_GenerateContent(page));
432 FPDF_BITMAP page_bitmap = RenderPage(page);
433#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700434 const char md5[] = "a4dddc1a3930fa694bbff9789dab4161";
Nicolas Pena49058402017-02-14 18:26:20 -0500435#else
Lei Zhang3de50052017-03-29 21:02:13 -0700436 const char md5[] = "6e8a9b0682f60fd3ff1bf087b093d30d";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400437#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700438 CompareBitmap(page_bitmap, 612, 792, md5);
Nicolas Pena49058402017-02-14 18:26:20 -0500439 FPDFBitmap_Destroy(page_bitmap);
440
441 // Try another font
442 FPDF_PAGEOBJECT text2 =
Nicolas Penad03ca422017-03-06 13:54:33 -0500443 FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500444 EXPECT_TRUE(text2);
445 EXPECT_TRUE(FPDFText_SetText(text2, "Hi, I'm Bold. Times New Roman Bold."));
446 FPDFPageObj_Transform(text2, 1, 0, 0, 1, 100, 600);
447 FPDFPage_InsertObject(page, text2);
448 EXPECT_TRUE(FPDFPage_GenerateContent(page));
449 page_bitmap = RenderPage(page);
450#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700451 const char md5_2[] = "a5c4ace4c6f27644094813fe1441a21c";
Lei Zhang3de50052017-03-29 21:02:13 -0700452#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
453 const char md5_2[] = "56863642d4d8b418cfd810fdb5a5d92f";
Nicolas Pena49058402017-02-14 18:26:20 -0500454#else
Lei Zhang3de50052017-03-29 21:02:13 -0700455 const char md5_2[] = "0c83875429688bda45a55a692d5aa781";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400456#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700457 CompareBitmap(page_bitmap, 612, 792, md5_2);
Nicolas Pena49058402017-02-14 18:26:20 -0500458 FPDFBitmap_Destroy(page_bitmap);
459
460 // And some randomly transformed text
Nicolas Penad03ca422017-03-06 13:54:33 -0500461 FPDF_PAGEOBJECT text3 =
462 FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
Nicolas Pena49058402017-02-14 18:26:20 -0500463 EXPECT_TRUE(text3);
464 EXPECT_TRUE(FPDFText_SetText(text3, "Can you read me? <:)>"));
465 FPDFPageObj_Transform(text3, 1, 1.5, 2, 0.5, 200, 200);
466 FPDFPage_InsertObject(page, text3);
467 EXPECT_TRUE(FPDFPage_GenerateContent(page));
468 page_bitmap = RenderPage(page);
469#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
Lei Zhang0d6d1782017-03-24 15:52:00 -0700470 const char md5_3[] = "40b3ef04f915ff4c4208948001763544";
Lei Zhang3de50052017-03-29 21:02:13 -0700471#elif _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
472 const char md5_3[] = "174f30c36c73adb265c0f6ddb3a61c87";
Nicolas Pena49058402017-02-14 18:26:20 -0500473#else
Lei Zhang3de50052017-03-29 21:02:13 -0700474 const char md5_3[] = "abfa9246d60f821b5b7638efd32dfd92";
Nicolas Pena5bcd9a32017-03-22 11:04:35 -0400475#endif
Lei Zhang0d6d1782017-03-24 15:52:00 -0700476 CompareBitmap(page_bitmap, 612, 792, md5_3);
Nicolas Pena49058402017-02-14 18:26:20 -0500477 FPDFBitmap_Destroy(page_bitmap);
478
479 // TODO(npm): Why are there issues with text rotated by 90 degrees?
480 // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
481 FPDF_ClosePage(page);
Nicolas Pena49058402017-02-14 18:26:20 -0500482}
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500483
484TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
485 // Start with a blank page
Nicolas Penad03ca422017-03-06 13:54:33 -0500486 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500487
488 // Add a red rectangle with some non-default alpha
489 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
490 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 255, 0, 0, 128));
491 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
492 FPDFPage_InsertObject(page, rect);
493 EXPECT_TRUE(FPDFPage_GenerateContent(page));
494
495 // Check the ExtGState
496 CPDF_Page* the_page = CPDFPageFromFPDFPage(page);
497 CPDF_Dictionary* graphics_dict =
498 the_page->m_pResources->GetDictFor("ExtGState");
499 ASSERT_TRUE(graphics_dict);
500 EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
501
502 // Check the bitmap
503 FPDF_BITMAP page_bitmap = RenderPage(page);
504 CompareBitmap(page_bitmap, 612, 792, "5384da3406d62360ffb5cac4476fff1c");
505 FPDFBitmap_Destroy(page_bitmap);
506
507 // Never mind, my new favorite color is blue, increase alpha
508 EXPECT_TRUE(FPDFPath_SetFillColor(rect, 0, 0, 255, 180));
509 EXPECT_TRUE(FPDFPage_GenerateContent(page));
510 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
511
512 // Check that bitmap displays changed content
513 page_bitmap = RenderPage(page);
514 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
515 FPDFBitmap_Destroy(page_bitmap);
516
517 // And now generate, without changes
518 EXPECT_TRUE(FPDFPage_GenerateContent(page));
519 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
520 page_bitmap = RenderPage(page);
521 CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
522 FPDFBitmap_Destroy(page_bitmap);
523
524 // Add some text to the page
Nicolas Penad03ca422017-03-06 13:54:33 -0500525 FPDF_PAGEOBJECT text = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500526 EXPECT_TRUE(FPDFText_SetText(text, "Something something #text# something"));
527 FPDFPageObj_Transform(text, 1, 0, 0, 1, 300, 300);
528 FPDFPage_InsertObject(page, text);
529 EXPECT_TRUE(FPDFPage_GenerateContent(page));
530 CPDF_Dictionary* font_dict = the_page->m_pResources->GetDictFor("Font");
531 ASSERT_TRUE(font_dict);
532 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
533
534 // Generate yet again, check dicts are reasonably sized
535 EXPECT_TRUE(FPDFPage_GenerateContent(page));
536 EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
537 EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
538 FPDF_ClosePage(page);
Nicolas Penaa4ad01f2017-02-15 16:26:48 -0500539}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500540
Nicolas Penad03ca422017-03-06 13:54:33 -0500541TEST_F(FPDFEditEmbeddertest, LoadSimpleType1Font) {
542 CreateNewDocument();
543 // TODO(npm): use other fonts after disallowing loading any font as any type
544 const CPDF_Font* stock_font =
545 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
546 const uint8_t* data = stock_font->m_Font.GetFontData();
547 const uint32_t size = stock_font->m_Font.GetSize();
548 FPDF_FONT font =
549 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, false);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500550 ASSERT_TRUE(font);
Nicolas Penad03ca422017-03-06 13:54:33 -0500551 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
552 EXPECT_TRUE(typed_font->IsType1Font());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500553
Nicolas Penad03ca422017-03-06 13:54:33 -0500554 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
Nicolas Penabe90aae2017-02-27 10:41:41 -0500555 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
556 EXPECT_EQ("Type1", font_dict->GetStringFor("Subtype"));
557 EXPECT_EQ("Times New Roman Bold", font_dict->GetStringFor("BaseFont"));
558 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
559 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
560 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
Nicolas Penad03ca422017-03-06 13:54:33 -0500561 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
562
Nicolas Penabe90aae2017-02-27 10:41:41 -0500563 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
Nicolas Penad03ca422017-03-06 13:54:33 -0500564 ASSERT_TRUE(widths_array);
565 ASSERT_EQ(224U, widths_array->GetCount());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500566 EXPECT_EQ(250, widths_array->GetNumberAt(0));
Nicolas Penad03ca422017-03-06 13:54:33 -0500567 EXPECT_EQ(569, widths_array->GetNumberAt(11));
568 EXPECT_EQ(500, widths_array->GetNumberAt(223));
569 CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, size, data);
570}
Nicolas Penabe90aae2017-02-27 10:41:41 -0500571
Nicolas Penad03ca422017-03-06 13:54:33 -0500572TEST_F(FPDFEditEmbeddertest, LoadSimpleTrueTypeFont) {
573 CreateNewDocument();
574 const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
575 const uint8_t* data = stock_font->m_Font.GetFontData();
576 const uint32_t size = stock_font->m_Font.GetSize();
577 FPDF_FONT font =
578 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, false);
579 ASSERT_TRUE(font);
580 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
581 EXPECT_TRUE(typed_font->IsTrueTypeFont());
Nicolas Penabe90aae2017-02-27 10:41:41 -0500582
Nicolas Penad03ca422017-03-06 13:54:33 -0500583 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
584 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
585 EXPECT_EQ("TrueType", font_dict->GetStringFor("Subtype"));
586 EXPECT_EQ("Courier New", font_dict->GetStringFor("BaseFont"));
587 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
588 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
589 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
590 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
Nicolas Penabe90aae2017-02-27 10:41:41 -0500591
Nicolas Penad03ca422017-03-06 13:54:33 -0500592 CPDF_Array* widths_array = font_dict->GetArrayFor("Widths");
593 ASSERT_TRUE(widths_array);
594 ASSERT_EQ(224U, widths_array->GetCount());
595 EXPECT_EQ(600, widths_array->GetNumberAt(33));
596 EXPECT_EQ(600, widths_array->GetNumberAt(74));
597 EXPECT_EQ(600, widths_array->GetNumberAt(223));
598 CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, size, data);
599}
600
601TEST_F(FPDFEditEmbeddertest, LoadCIDType0Font) {
602 CreateNewDocument();
603 const CPDF_Font* stock_font =
604 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
605 const uint8_t* data = stock_font->m_Font.GetFontData();
606 const uint32_t size = stock_font->m_Font.GetSize();
607 FPDF_FONT font =
608 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TYPE1, 1);
609 ASSERT_TRUE(font);
610 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
611 EXPECT_TRUE(typed_font->IsCIDFont());
612
613 // Check font dictionary entries
614 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
615 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
616 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
617 EXPECT_EQ("Times New Roman-Identity-H", font_dict->GetStringFor("BaseFont"));
618 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
619 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
620 ASSERT_TRUE(descendant_array);
621 EXPECT_EQ(1U, descendant_array->GetCount());
622
623 // Check the CIDFontDict
624 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
625 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
626 EXPECT_EQ("CIDFontType0", cidfont_dict->GetStringFor("Subtype"));
627 EXPECT_EQ("Times New Roman", cidfont_dict->GetStringFor("BaseFont"));
628 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
629 ASSERT_TRUE(cidinfo_dict);
630 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
631 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
632 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
633 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TYPE1, false, false, size, data);
634
635 // Check widths
636 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
637 ASSERT_TRUE(widths_array);
638 // Note: widths can be described in different ways in the widths array. The
639 // following checks are specific to our current implementation.
640 EXPECT_EQ(32, widths_array->GetNumberAt(0));
641 CPDF_Array* arr = widths_array->GetArrayAt(1);
642 ASSERT_TRUE(arr);
643 // This font support chars 32 to 126
644 EXPECT_EQ(95U, arr->GetCount());
645 EXPECT_EQ(250, arr->GetNumberAt(0));
646 EXPECT_EQ(610, arr->GetNumberAt(44));
647 EXPECT_EQ(541, arr->GetNumberAt(94));
648 // Next range: 160 - 383
649 EXPECT_EQ(160, widths_array->GetNumberAt(2));
650 arr = widths_array->GetArrayAt(3);
651 ASSERT_TRUE(arr);
652
653 CheckCompositeFontWidths(widths_array, typed_font);
654}
655
656TEST_F(FPDFEditEmbeddertest, LoadCIDType2Font) {
657 CreateNewDocument();
658 const CPDF_Font* stock_font =
659 CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
660 const uint8_t* data = stock_font->m_Font.GetFontData();
661 const uint32_t size = stock_font->m_Font.GetSize();
662
663 FPDF_FONT font =
664 FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 1);
665 ASSERT_TRUE(font);
666 CPDF_Font* typed_font = reinterpret_cast<CPDF_Font*>(font);
667 EXPECT_TRUE(typed_font->IsCIDFont());
668
669 // Check font dictionary entries
670 CPDF_Dictionary* font_dict = typed_font->GetFontDict();
671 EXPECT_EQ("Font", font_dict->GetStringFor("Type"));
672 EXPECT_EQ("Type0", font_dict->GetStringFor("Subtype"));
673 EXPECT_EQ("Arial Italic", font_dict->GetStringFor("BaseFont"));
674 EXPECT_EQ("Identity-H", font_dict->GetStringFor("Encoding"));
675 CPDF_Array* descendant_array = font_dict->GetArrayFor("DescendantFonts");
676 ASSERT_TRUE(descendant_array);
677 EXPECT_EQ(1U, descendant_array->GetCount());
678
679 // Check the CIDFontDict
680 CPDF_Dictionary* cidfont_dict = descendant_array->GetDictAt(0);
681 EXPECT_EQ("Font", cidfont_dict->GetStringFor("Type"));
682 EXPECT_EQ("CIDFontType2", cidfont_dict->GetStringFor("Subtype"));
683 EXPECT_EQ("Arial Italic", cidfont_dict->GetStringFor("BaseFont"));
684 CPDF_Dictionary* cidinfo_dict = cidfont_dict->GetDictFor("CIDSystemInfo");
685 ASSERT_TRUE(cidinfo_dict);
686 EXPECT_EQ("Adobe", cidinfo_dict->GetStringFor("Registry"));
687 EXPECT_EQ("Identity", cidinfo_dict->GetStringFor("Ordering"));
688 EXPECT_EQ(0, cidinfo_dict->GetNumberFor("Supplement"));
689 CheckFontDescriptor(cidfont_dict, FPDF_FONT_TRUETYPE, false, true, size,
690 data);
691
692 // Check widths
693 CPDF_Array* widths_array = cidfont_dict->GetArrayFor("W");
694 ASSERT_TRUE(widths_array);
695 CheckCompositeFontWidths(widths_array, typed_font);
Nicolas Penabe90aae2017-02-27 10:41:41 -0500696}
rbpotterce8e51e2017-04-28 12:42:47 -0700697
698TEST_F(FPDFEditEmbeddertest, NormalizeNegativeRotation) {
699 // Load document with a -90 degree rotation
700 EXPECT_TRUE(OpenDocument("bug_713197.pdf"));
701 FPDF_PAGE page = LoadPage(0);
702 EXPECT_NE(nullptr, page);
703
704 EXPECT_EQ(3, FPDFPage_GetRotation(page));
705 UnloadPage(page);
706}