Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpdf_pagecontentgenerator_unittest.cpp
Go to the documentation of this file.
1// Copyright 2017 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
6
7#include <iterator>
8#include <memory>
9#include <utility>
10
11#include "core/fpdfapi/font/cpdf_font.h"
12#include "core/fpdfapi/page/cpdf_colorspace.h"
13#include "core/fpdfapi/page/cpdf_docpagedata.h"
14#include "core/fpdfapi/page/cpdf_form.h"
15#include "core/fpdfapi/page/cpdf_page.h"
16#include "core/fpdfapi/page/cpdf_pathobject.h"
17#include "core/fpdfapi/page/cpdf_textobject.h"
18#include "core/fpdfapi/page/cpdf_textstate.h"
19#include "core/fpdfapi/page/test_with_page_module.h"
20#include "core/fpdfapi/parser/cpdf_dictionary.h"
21#include "core/fpdfapi/parser/cpdf_document.h"
22#include "core/fpdfapi/parser/cpdf_name.h"
23#include "core/fpdfapi/parser/cpdf_parser.h"
24#include "core/fpdfapi/parser/cpdf_reference.h"
25#include "core/fpdfapi/parser/cpdf_stream.h"
26#include "core/fpdfapi/parser/cpdf_test_document.h"
27#include "core/fxcrt/data_vector.h"
28#include "core/fxge/cfx_fillrenderoptions.h"
29#include "testing/gtest/include/gtest/gtest.h"
30
32 protected:
34 fxcrt::ostringstream* buf,
35 CPDF_PathObject* pPathObj) {
36 pGen->ProcessPath(buf, pPathObj);
37 }
38
39 RetainPtr<const CPDF_Dictionary> TestGetResource(
41 const ByteString& type,
42 const ByteString& name) {
43 RetainPtr<const CPDF_Dictionary> pResources =
44 pGen->m_pObjHolder->GetResources();
45 return pResources->GetDictFor(type)->GetDictFor(name);
46 }
47
49 fxcrt::ostringstream* buf,
50 CPDF_TextObject* pTextObj) {
51 pGen->ProcessText(buf, pTextObj);
52 }
53};
54
56 auto pPathObj = std::make_unique<CPDF_PathObject>();
57 pPathObj->set_stroke(true);
58 pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kEvenOdd);
59 pPathObj->path().AppendRect(10, 5, 13, 30);
60
61 auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
62 auto pTestPage = pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict);
63 CPDF_PageContentGenerator generator(pTestPage.Get());
64 fxcrt::ostringstream buf;
65 TestProcessPath(&generator, &buf, pPathObj.get());
66 EXPECT_EQ("q 1 0 0 1 0 0 cm 10 5 3 25 re B* Q\n", ByteString(buf));
67
68 pPathObj = std::make_unique<CPDF_PathObject>();
69 pPathObj->path().AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove);
70 pPathObj->path().AppendPoint(CFX_PointF(5.2f, 0),
72 pPathObj->path().AppendPoint(CFX_PointF(5.2f, 3.78f),
74 pPathObj->path().AppendPointAndClose(CFX_PointF(0, 3.78f),
76 buf.str("");
77 TestProcessPath(&generator, &buf, pPathObj.get());
78 EXPECT_EQ("q 1 0 0 1 0 0 cm 0 0 5.1999998 3.78 re n Q\n", ByteString(buf));
79}
80
82 static const std::vector<float> rgb = {0.000000000000000000001f, 0.7f, 0.35f};
84 CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
85 {
86 auto pPathObj = std::make_unique<CPDF_PathObject>();
87 pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
88
89 // Test code in ProcessPath that generates re operator
90 pPathObj->path().AppendRect(0.000000000000000000001,
91 0.000000000000000000001, 100, 100);
92
93 pPathObj->mutable_color_state().SetFillColor(pCS, rgb);
94 pPathObj->mutable_color_state().SetStrokeColor(pCS, rgb);
95 pPathObj->mutable_graph_state().SetLineWidth(200000000000000000001.0);
96 pPathObj->Transform(CFX_Matrix(1, 0, 0, 1, 0.000000000000000000001,
97 200000000000000.000002));
98
99 auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
100 auto pTestPage = pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict);
101 CPDF_PageContentGenerator generator(pTestPage.Get());
102 fxcrt::ostringstream buf;
103 TestProcessPath(&generator, &buf, pPathObj.get());
104 EXPECT_EQ(
105 "q 0 0.701961 0.34902 rg 0 0.701961 0.34902 RG 200000000000000000000 w"
106 " 1 0 0 1 .00000000000000000000099999997 200000000000000 cm .000000000"
107 "00000000000099999997 .00000000000000000000099999997 100 100 re f Q\n",
108 ByteString(buf));
109 }
110
111 {
112 // Test code in ProcessPath that handles bezier operator
113 auto pPathObj = std::make_unique<CPDF_PathObject>();
114 pPathObj->mutable_color_state().SetFillColor(pCS, rgb);
115 pPathObj->mutable_color_state().SetStrokeColor(pCS, rgb);
116 pPathObj->mutable_graph_state().SetLineWidth(2.000000000000000000001);
117 pPathObj->Transform(CFX_Matrix(1, 0, 0, 1, 432, 500000000000000.000002));
118
119 pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
120 pPathObj->path().AppendPoint(CFX_PointF(0.000000000000000000001f, 4.67f),
122 pPathObj->path().AppendPoint(
123 CFX_PointF(0.000000000000000000001, 100000000000000.000002),
125 pPathObj->path().AppendPoint(CFX_PointF(0.0000000000001f, 3.15f),
127 pPathObj->path().AppendPoint(CFX_PointF(3.57f, 2.98f),
129 pPathObj->path().AppendPointAndClose(
130 CFX_PointF(53.4f, 5000000000000000000.00000000000000004),
132 auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
133 auto pTestPage = pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict);
134 CPDF_PageContentGenerator generator(pTestPage.Get());
135 fxcrt::ostringstream buf;
136
137 TestProcessPath(&generator, &buf, pPathObj.get());
138 EXPECT_EQ(
139 "q 0 0.701961 0.34902 rg 0 0.701961 0.34902 RG 2 w 1 0 0 1 432 4999999"
140 "90000000 cm .00000000000000000000099999997 4.6700001 m .0000000000000"
141 "0000000099999997 100000000000000 l .000000000000099999998 3.1500001 3"
142 ".5699999 2.98 53.400002 5000000000000000000 c h f Q\n",
143 ByteString(buf));
144 }
145}
146
148 auto pPathObj = std::make_unique<CPDF_PathObject>();
149 pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
150 pPathObj->path().AppendPoint(CFX_PointF(3.102f, 4.67f),
152 pPathObj->path().AppendPoint(CFX_PointF(5.45f, 0.29f),
154 pPathObj->path().AppendPoint(CFX_PointF(4.24f, 3.15f),
156 pPathObj->path().AppendPoint(CFX_PointF(4.65f, 2.98f),
158 pPathObj->path().AppendPoint(CFX_PointF(3.456f, 0.24f),
160 pPathObj->path().AppendPoint(CFX_PointF(10.6f, 11.15f),
162 pPathObj->path().AppendPoint(CFX_PointF(11, 12.5f),
164 pPathObj->path().AppendPoint(CFX_PointF(11.46f, 12.67f),
166 pPathObj->path().AppendPoint(CFX_PointF(11.84f, 12.96f),
168 pPathObj->path().AppendPointAndClose(CFX_PointF(12, 13.64f),
170
171 auto dummy_page_dict = pdfium::MakeRetain<CPDF_Dictionary>();
172 auto pTestPage = pdfium::MakeRetain<CPDF_Page>(nullptr, dummy_page_dict);
173 CPDF_PageContentGenerator generator(pTestPage.Get());
174 fxcrt::ostringstream buf;
175 TestProcessPath(&generator, &buf, pPathObj.get());
176 EXPECT_EQ(
177 "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4499998 .28999999 l 4.2399998 "
178 "3.1500001 4.6500001 2.98 3.4560001 .23999999 c 10.6000004 11.149999"
179 "6 l 11 12.5 l 11.46 12.6700001 11.8400002 12.96 12 13.6400003 c h f"
180 " Q\n",
181 ByteString(buf));
182}
183
185 auto pPathObj = std::make_unique<CPDF_PathObject>();
186 pPathObj->set_stroke(true);
187 pPathObj->set_filltype(CFX_FillRenderOptions::FillType::kWinding);
188 pPathObj->path().AppendPoint(CFX_PointF(1, 2), CFX_Path::Point::Type::kMove);
189 pPathObj->path().AppendPoint(CFX_PointF(3, 4), CFX_Path::Point::Type::kLine);
190 pPathObj->path().AppendPointAndClose(CFX_PointF(5, 6),
192
193 static const std::vector<float> rgb = {0.5f, 0.7f, 0.35f};
195 CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
196 pPathObj->mutable_color_state().SetFillColor(pCS, rgb);
197
198 static const std::vector<float> rgb2 = {1, 0.9f, 0};
199 pPathObj->mutable_color_state().SetStrokeColor(pCS, rgb2);
200 pPathObj->mutable_general_state().SetFillAlpha(0.5f);
201 pPathObj->mutable_general_state().SetStrokeAlpha(0.8f);
202
203 auto pDoc = std::make_unique<CPDF_TestDocument>();
204 pDoc->CreateNewDoc();
205
206 RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(0));
207 auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
208 CPDF_PageContentGenerator generator(pTestPage.Get());
209 fxcrt::ostringstream buf;
210 TestProcessPath(&generator, &buf, pPathObj.get());
211 ByteString pathString(buf);
212
213 // Color RGB values used are integers divided by 255.
214 EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /",
215 pathString.First(48));
216 EXPECT_EQ(" gs 1 0 0 1 0 0 cm 1 2 m 3 4 l 5 6 l h B Q\n",
217 pathString.Last(43));
218 ASSERT_GT(pathString.GetLength(), 91U);
219 RetainPtr<const CPDF_Dictionary> externalGS =
220 TestGetResource(&generator, "ExtGState",
221 pathString.Substr(48, pathString.GetLength() - 91));
222 ASSERT_TRUE(externalGS);
223 EXPECT_EQ(0.5f, externalGS->GetFloatFor("ca"));
224 EXPECT_EQ(0.8f, externalGS->GetFloatFor("CA"));
225
226 // Same path, now with a stroke.
227 pPathObj->mutable_graph_state().SetLineWidth(10.5f);
228 buf.str("");
229 TestProcessPath(&generator, &buf, pPathObj.get());
230 ByteString pathString2(buf);
231 EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG 10.5 w /",
232 pathString2.First(55));
233 EXPECT_EQ(" gs 1 0 0 1 0 0 cm 1 2 m 3 4 l 5 6 l h B Q\n",
234 pathString2.Last(43));
235
236 // Compare with the previous (should use same dictionary for gs)
237 EXPECT_EQ(pathString.GetLength() + 7, pathString2.GetLength());
238 EXPECT_EQ(pathString.Substr(48, pathString.GetLength() - 76),
239 pathString2.Substr(55, pathString2.GetLength() - 83));
240}
241
243 // Checking font whose font dictionary is not yet indirect object.
244 auto pDoc = std::make_unique<CPDF_TestDocument>();
245 pDoc->CreateNewDoc();
246
247 RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(0));
248 auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
249 CPDF_PageContentGenerator generator(pTestPage.Get());
250 auto pTextObj = std::make_unique<CPDF_TextObject>();
251 pTextObj->mutable_text_state().SetFont(
252 CPDF_Font::GetStockFont(pDoc.get(), "Times-Roman"));
253 pTextObj->mutable_text_state().SetFontSize(10.0f);
254
255 static const std::vector<float> rgb = {0.5f, 0.7f, 0.35f};
257 CPDF_ColorSpace::GetStockCS(CPDF_ColorSpace::Family::kDeviceRGB);
258 pTextObj->mutable_color_state().SetFillColor(pCS, rgb);
259
260 static const std::vector<float> rgb2 = {1, 0.9f, 0};
261 pTextObj->mutable_color_state().SetStrokeColor(pCS, rgb2);
262 pTextObj->mutable_general_state().SetFillAlpha(0.5f);
263 pTextObj->mutable_general_state().SetStrokeAlpha(0.8f);
264 pTextObj->Transform(CFX_Matrix(1, 0, 0, 1, 100, 100));
265 pTextObj->SetText("Hello World");
266 fxcrt::ostringstream buf;
267 TestProcessText(&generator, &buf, pTextObj.get());
268 ByteString textString(buf);
269 auto firstResourceAt = textString.Find('/');
270 ASSERT_TRUE(firstResourceAt.has_value());
271 firstResourceAt = firstResourceAt.value() + 1;
272 auto secondResourceAt = textString.ReverseFind('/');
273 ASSERT_TRUE(secondResourceAt.has_value());
274 secondResourceAt = secondResourceAt.value() + 1;
275 ByteString firstString = textString.First(firstResourceAt.value());
276 ByteString midString =
277 textString.Substr(firstResourceAt.value(),
278 secondResourceAt.value() - firstResourceAt.value());
279 ByteString lastString =
280 textString.Last(textString.GetLength() - secondResourceAt.value());
281 // q and Q must be outside the BT .. ET operations
282 ByteString compareString1 =
283 "q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /";
284 // Color RGB values used are integers divided by 255.
285 ByteString compareString2 = " gs BT 1 0 0 1 100 100 Tm /";
286 ByteString compareString3 = " 10 Tf 0 Tr <48656C6C6F20576F726C64> Tj ET Q\n";
287 EXPECT_LT(compareString1.GetLength() + compareString2.GetLength() +
288 compareString3.GetLength(),
289 textString.GetLength());
290 EXPECT_EQ(compareString1, firstString.First(compareString1.GetLength()));
291 EXPECT_EQ(compareString2, midString.Last(compareString2.GetLength()));
292 EXPECT_EQ(compareString3, lastString.Last(compareString3.GetLength()));
293 RetainPtr<const CPDF_Dictionary> externalGS = TestGetResource(
294 &generator, "ExtGState",
295 midString.First(midString.GetLength() - compareString2.GetLength()));
296 ASSERT_TRUE(externalGS);
297 EXPECT_EQ(0.5f, externalGS->GetFloatFor("ca"));
298 EXPECT_EQ(0.8f, externalGS->GetFloatFor("CA"));
299 RetainPtr<const CPDF_Dictionary> fontDict = TestGetResource(
300 &generator, "Font",
301 lastString.First(lastString.GetLength() - compareString3.GetLength()));
302 ASSERT_TRUE(fontDict);
303 EXPECT_EQ("Font", fontDict->GetNameFor("Type"));
304 EXPECT_EQ("Type1", fontDict->GetNameFor("Subtype"));
305 EXPECT_EQ("Times-Roman", fontDict->GetNameFor("BaseFont"));
306}
307
309 // Checking font whose font dictionary is already an indirect object.
310 auto pDoc = std::make_unique<CPDF_TestDocument>();
311 pDoc->CreateNewDoc();
312
313 RetainPtr<CPDF_Dictionary> pPageDict(pDoc->CreateNewPage(0));
314 auto pTestPage = pdfium::MakeRetain<CPDF_Page>(pDoc.get(), pPageDict);
315 CPDF_PageContentGenerator generator(pTestPage.Get());
316
317 fxcrt::ostringstream buf;
318 {
319 // Set the text object font and text
320 auto pTextObj = std::make_unique<CPDF_TextObject>();
321 auto pDict = pDoc->NewIndirect<CPDF_Dictionary>();
322 pDict->SetNewFor<CPDF_Name>("Type", "Font");
323 pDict->SetNewFor<CPDF_Name>("Subtype", "TrueType");
324
325 RetainPtr<CPDF_Font> pFont = CPDF_Font::GetStockFont(pDoc.get(), "Arial");
326 pDict->SetNewFor<CPDF_Name>("BaseFont", pFont->GetBaseFontName());
327
328 auto pDesc = pDoc->NewIndirect<CPDF_Dictionary>();
329 pDesc->SetNewFor<CPDF_Name>("Type", "FontDescriptor");
330 pDesc->SetNewFor<CPDF_Name>("FontName", pFont->GetBaseFontName());
331 pDict->SetNewFor<CPDF_Reference>("FontDescriptor", pDoc.get(),
332 pDesc->GetObjNum());
333
334 pTextObj->mutable_text_state().SetFont(
335 CPDF_DocPageData::FromDocument(pDoc.get())->GetFont(pDict));
336 pTextObj->mutable_text_state().SetFontSize(15.5f);
337 pTextObj->SetText("I am indirect");
338 pTextObj->SetTextRenderMode(TextRenderingMode::MODE_FILL_CLIP);
339
340 // Add a clipping path.
341 auto pPath = std::make_unique<CPDF_Path>();
342 pPath->AppendPoint(CFX_PointF(0, 0), CFX_Path::Point::Type::kMove);
343 pPath->AppendPoint(CFX_PointF(5, 0), CFX_Path::Point::Type::kLine);
344 pPath->AppendPoint(CFX_PointF(5, 4), CFX_Path::Point::Type::kLine);
345 pPath->AppendPointAndClose(CFX_PointF(0, 4), CFX_Path::Point::Type::kLine);
346 CPDF_ClipPath& clip_path = pTextObj->mutable_clip_path();
347 clip_path.Emplace();
349
350 TestProcessText(&generator, &buf, pTextObj.get());
351 }
352
353 ByteString textString(buf);
354 auto firstResourceAt = textString.Find('/');
355 ASSERT_TRUE(firstResourceAt.has_value());
356 firstResourceAt = firstResourceAt.value() + 1;
357 ByteString firstString = textString.First(firstResourceAt.value());
358 ByteString lastString =
359 textString.Last(textString.GetLength() - firstResourceAt.value());
360 // q and Q must be outside the BT .. ET operations
361 ByteString compareString1 = "q 0 0 5 4 re W* n BT 1 0 0 1 0 0 Tm /";
362 ByteString compareString2 =
363 " 15.5 Tf 4 Tr <4920616D20696E646972656374> Tj ET Q\n";
364 EXPECT_LT(compareString1.GetLength() + compareString2.GetLength(),
365 textString.GetLength());
366 EXPECT_EQ(compareString1, textString.First(compareString1.GetLength()));
367 EXPECT_EQ(compareString2, textString.Last(compareString2.GetLength()));
368 RetainPtr<const CPDF_Dictionary> fontDict = TestGetResource(
369 &generator, "Font",
370 textString.Substr(compareString1.GetLength(),
371 textString.GetLength() - compareString1.GetLength() -
372 compareString2.GetLength()));
373 ASSERT_TRUE(fontDict);
374 EXPECT_TRUE(fontDict->GetObjNum());
375 EXPECT_EQ("Font", fontDict->GetNameFor("Type"));
376 EXPECT_EQ("TrueType", fontDict->GetNameFor("Subtype"));
377 EXPECT_EQ("Helvetica", fontDict->GetNameFor("BaseFont"));
378 RetainPtr<const CPDF_Dictionary> fontDesc =
379 fontDict->GetDictFor("FontDescriptor");
380 ASSERT_TRUE(fontDesc);
381 EXPECT_TRUE(fontDesc->GetObjNum());
382 EXPECT_EQ("FontDescriptor", fontDesc->GetNameFor("Type"));
383 EXPECT_EQ("Helvetica", fontDesc->GetNameFor("FontName"));
384}
385
387 auto pDoc = std::make_unique<CPDF_TestDocument>();
388 pDoc->CreateNewDoc();
389 auto pStream =
390 pdfium::MakeRetain<CPDF_Stream>(pdfium::MakeRetain<CPDF_Dictionary>());
391
392 // Create an empty form.
393 auto pTestForm = std::make_unique<CPDF_Form>(pDoc.get(), nullptr, pStream);
394 pTestForm->ParseContent();
396 pTestForm->GetParseState());
397
398 // The generated stream for the empty form should be an empty string.
399 CPDF_PageContentGenerator generator(pTestForm.get());
400 fxcrt::ostringstream buf;
401 generator.ProcessPageObjects(&buf);
402 EXPECT_EQ("", ByteString(buf));
403}
404
406 auto pDoc = std::make_unique<CPDF_TestDocument>();
407 pDoc->CreateNewDoc();
408 static constexpr uint8_t kContents[] =
409 "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4500012 .28999999 "
410 "l 4.2399998 3.1499999 4.65 2.98 3.456 0.24 c 3.102 4.6700001 l h f Q\n";
411 auto pStream = pdfium::MakeRetain<CPDF_Stream>(
412 DataVector<uint8_t>(std::begin(kContents), std::end(kContents)),
413 pdfium::MakeRetain<CPDF_Dictionary>());
414
415 // Create a form with a non-empty stream.
416 auto pTestForm = std::make_unique<CPDF_Form>(pDoc.get(), nullptr, pStream);
417 pTestForm->ParseContent();
419 pTestForm->GetParseState());
420
421 CPDF_PageContentGenerator generator(pTestForm.get());
422 fxcrt::ostringstream process_buf;
423 generator.ProcessPageObjects(&process_buf);
424 EXPECT_STREQ(
425 "q 1 0 0 1 0 0 cm 3.102 4.6700001 m 5.4500012 .28999999 l 4.2399998 3.14"
426 "99999 4.6500001 2.98 3.4560001 .24000001 c 3.102 4.6700001 l h f Q\n",
427 ByteString(process_buf).c_str());
428}
CFX_Matrix(float a1, float b1, float c1, float d1, float e1, float f1)
void AppendPath(CPDF_Path path, CFX_FillRenderOptions::FillType type)
static CPDF_DocPageData * FromDocument(const CPDF_Document *pDoc)
static RetainPtr< CPDF_Font > GetStockFont(CPDF_Document *pDoc, ByteStringView fontname)
RetainPtr< const CPDF_Dictionary > TestGetResource(CPDF_PageContentGenerator *pGen, const ByteString &type, const ByteString &name)
void TestProcessText(CPDF_PageContentGenerator *pGen, fxcrt::ostringstream *buf, CPDF_TextObject *pTextObj)
void TestProcessPath(CPDF_PageContentGenerator *pGen, fxcrt::ostringstream *buf, CPDF_PathObject *pPathObj)
const char * c_str() const
Definition bytestring.h:76
TEST_F(CPDF_PageContentGeneratorTest, ProcessRect)
TextRenderingMode