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.cpp
Go to the documentation of this file.
1// Copyright 2016 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// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
8
9#include <map>
10#include <memory>
11#include <set>
12#include <sstream>
13#include <tuple>
14#include <utility>
15
16#include "constants/page_object.h"
17#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
18#include "core/fpdfapi/edit/cpdf_pagecontentmanager.h"
19#include "core/fpdfapi/edit/cpdf_stringarchivestream.h"
20#include "core/fpdfapi/font/cpdf_truetypefont.h"
21#include "core/fpdfapi/font/cpdf_type1font.h"
22#include "core/fpdfapi/page/cpdf_contentmarks.h"
23#include "core/fpdfapi/page/cpdf_docpagedata.h"
24#include "core/fpdfapi/page/cpdf_form.h"
25#include "core/fpdfapi/page/cpdf_formobject.h"
26#include "core/fpdfapi/page/cpdf_image.h"
27#include "core/fpdfapi/page/cpdf_imageobject.h"
28#include "core/fpdfapi/page/cpdf_page.h"
29#include "core/fpdfapi/page/cpdf_path.h"
30#include "core/fpdfapi/page/cpdf_pathobject.h"
31#include "core/fpdfapi/page/cpdf_textobject.h"
32#include "core/fpdfapi/parser/cpdf_array.h"
33#include "core/fpdfapi/parser/cpdf_dictionary.h"
34#include "core/fpdfapi/parser/cpdf_document.h"
35#include "core/fpdfapi/parser/cpdf_name.h"
36#include "core/fpdfapi/parser/cpdf_number.h"
37#include "core/fpdfapi/parser/cpdf_reference.h"
38#include "core/fpdfapi/parser/cpdf_stream.h"
39#include "core/fpdfapi/parser/fpdf_parser_decode.h"
40#include "core/fpdfapi/parser/fpdf_parser_utility.h"
41#include "core/fpdfapi/parser/object_tree_traversal_util.h"
42#include "third_party/base/check.h"
43#include "third_party/base/containers/contains.h"
44#include "third_party/base/containers/span.h"
45#include "third_party/base/notreached.h"
46#include "third_party/base/numerics/safe_conversions.h"
47
48namespace {
49
50// Key: The resource type.
51// Value: The resource names of a given type.
52using ResourcesMap = std::map<ByteString, std::set<ByteString>>;
53
54bool GetColor(const CPDF_Color* pColor, float* rgb) {
55 int intRGB[3];
56 if (!pColor || !pColor->IsColorSpaceRGB() ||
57 !pColor->GetRGB(&intRGB[0], &intRGB[1], &intRGB[2])) {
58 return false;
59 }
60 rgb[0] = intRGB[0] / 255.0f;
61 rgb[1] = intRGB[1] / 255.0f;
62 rgb[2] = intRGB[2] / 255.0f;
63 return true;
64}
65
66void RecordPageObjectResourceUsage(const CPDF_PageObject* page_object,
67 ResourcesMap& seen_resources) {
68 const ByteString& resource_name = page_object->GetResourceName();
69 if (!resource_name.IsEmpty()) {
70 switch (page_object->GetType()) {
72 seen_resources["Font"].insert(resource_name);
73 break;
76 seen_resources["XObject"].insert(resource_name);
77 break;
79 break;
81 break;
82 }
83 }
84 for (const auto& name : page_object->GetGraphicsResourceNames()) {
85 CHECK(!name.IsEmpty());
86 seen_resources["ExtGState"].insert(name);
87 }
88}
89
90void RemoveUnusedResources(RetainPtr<CPDF_Dictionary> resources_dict,
91 const ResourcesMap& resources_in_use) {
92 // TODO(thestig): Remove other unused resource types:
93 // - ColorSpace
94 // - Pattern
95 // - Shading
96 static constexpr const char* kResourceKeys[] = {"ExtGState", "Font",
97 "XObject"};
98 for (const char* resource_key : kResourceKeys) {
99 RetainPtr<CPDF_Dictionary> resource_dict =
100 resources_dict->GetMutableDictFor(resource_key);
101 if (!resource_dict) {
102 continue;
103 }
104
105 std::vector<ByteString> keys;
106 {
107 CPDF_DictionaryLocker resource_dict_locker(resource_dict);
108 for (auto& it : resource_dict_locker) {
109 keys.push_back(it.first);
110 }
111 }
112
113 auto it = resources_in_use.find(resource_key);
114 const std::set<ByteString>* resource_in_use_of_current_type =
115 it != resources_in_use.end() ? &it->second : nullptr;
116 for (const ByteString& key : keys) {
117 if (resource_in_use_of_current_type &&
118 pdfium::Contains(*resource_in_use_of_current_type, key)) {
119 continue;
120 }
121
122 resource_dict->RemoveFor(key.AsStringView());
123 }
124 }
125}
126
127} // namespace
128
130 CPDF_PageObjectHolder* pObjHolder)
132 for (const auto& pObj : *pObjHolder) {
133 if (pObj)
134 m_pageObjects.emplace_back(pObj.get());
135 }
136}
137
139
141 DCHECK(m_pObjHolder->IsPage());
142 std::map<int32_t, fxcrt::ostringstream> new_stream_data =
143 GenerateModifiedStreams();
144 // If no streams were regenerated or removed, nothing to do here.
145 if (new_stream_data.empty()) {
146 return;
147 }
148
149 UpdateContentStreams(std::move(new_stream_data));
150 UpdateResourcesDict();
151}
152
153std::map<int32_t, fxcrt::ostringstream>
154CPDF_PageContentGenerator::GenerateModifiedStreams() {
155 // Figure out which streams are dirty.
156 std::set<int32_t> all_dirty_streams;
157 for (auto& pPageObj : m_pageObjects) {
158 if (pPageObj->IsDirty())
159 all_dirty_streams.insert(pPageObj->GetContentStream());
160 }
161 std::set<int32_t> marked_dirty_streams = m_pObjHolder->TakeDirtyStreams();
162 all_dirty_streams.insert(marked_dirty_streams.begin(),
163 marked_dirty_streams.end());
164
165 // Start regenerating dirty streams.
166 std::map<int32_t, fxcrt::ostringstream> streams;
167 std::set<int32_t> empty_streams;
168 std::unique_ptr<const CPDF_ContentMarks> empty_content_marks =
169 std::make_unique<CPDF_ContentMarks>();
170 std::map<int32_t, const CPDF_ContentMarks*> current_content_marks;
171
172 for (int32_t dirty_stream : all_dirty_streams) {
173 fxcrt::ostringstream buf;
174
175 // Set the default graphic state values
176 buf << "q\n";
177 if (!m_pObjHolder->GetLastCTM().IsIdentity())
178 WriteMatrix(buf, m_pObjHolder->GetLastCTM().GetInverse()) << " cm\n";
179
180 ProcessDefaultGraphics(&buf);
181 streams[dirty_stream] = std::move(buf);
182 empty_streams.insert(dirty_stream);
183 current_content_marks[dirty_stream] = empty_content_marks.get();
184 }
185
186 // Process the page objects, write into each dirty stream.
187 for (auto& pPageObj : m_pageObjects) {
188 int stream_index = pPageObj->GetContentStream();
189 auto it = streams.find(stream_index);
190 if (it == streams.end())
191 continue;
192
193 fxcrt::ostringstream* buf = &it->second;
194 empty_streams.erase(stream_index);
195 current_content_marks[stream_index] =
196 ProcessContentMarks(buf, pPageObj, current_content_marks[stream_index]);
197 ProcessPageObject(buf, pPageObj);
198 }
199
200 // Finish dirty streams.
201 for (int32_t dirty_stream : all_dirty_streams) {
202 fxcrt::ostringstream* buf = &streams[dirty_stream];
203 if (pdfium::Contains(empty_streams, dirty_stream)) {
204 // Clear to show that this stream needs to be deleted.
205 buf->str("");
206 } else {
207 FinishMarks(buf, current_content_marks[dirty_stream]);
208
209 // Return graphics to original state
210 *buf << "Q\n";
211 }
212 }
213
214 return streams;
215}
216
217void CPDF_PageContentGenerator::UpdateContentStreams(
218 std::map<int32_t, fxcrt::ostringstream>&& new_stream_data) {
219 CHECK(!new_stream_data.empty());
220
221 // Make sure default graphics are created.
222 m_DefaultGraphicsName = GetOrCreateDefaultGraphics();
223
224 CPDF_PageContentManager page_content_manager(m_pObjHolder, m_pDocument);
225 for (auto& pair : new_stream_data) {
226 int32_t stream_index = pair.first;
227 fxcrt::ostringstream* buf = &pair.second;
228
229 if (stream_index == CPDF_PageObject::kNoContentStream) {
230 int new_stream_index =
231 pdfium::base::checked_cast<int>(page_content_manager.AddStream(buf));
232 UpdateStreamlessPageObjects(new_stream_index);
233 continue;
234 }
235
236 page_content_manager.UpdateStream(stream_index, buf);
237 }
238}
239
240void CPDF_PageContentGenerator::UpdateResourcesDict() {
241 RetainPtr<CPDF_Dictionary> resources = m_pObjHolder->GetMutableResources();
242 if (!resources) {
243 return;
244 }
245
246 const uint32_t resources_object_number = resources->GetObjNum();
247 if (resources_object_number) {
248 // If `resources` is not an inline object, then do not modify it directly if
249 // it has multiple references.
250 if (pdfium::Contains(GetObjectsWithMultipleReferences(m_pDocument),
251 resources_object_number)) {
252 resources = pdfium::WrapRetain(resources->Clone()->AsMutableDictionary());
253 const uint32_t clone_object_number =
254 m_pDocument->AddIndirectObject(resources);
255 m_pObjHolder->SetResources(resources);
256 m_pObjHolder->GetMutableDict()->SetNewFor<CPDF_Reference>(
257 pdfium::page_object::kResources, m_pDocument, clone_object_number);
258 }
259 }
260
261 ResourcesMap seen_resources;
262 for (auto& page_object : m_pageObjects) {
263 RecordPageObjectResourceUsage(page_object, seen_resources);
264 }
265 if (!m_DefaultGraphicsName.IsEmpty()) {
266 seen_resources["ExtGState"].insert(m_DefaultGraphicsName);
267 }
268
269 RemoveUnusedResources(std::move(resources), seen_resources);
270}
271
272ByteString CPDF_PageContentGenerator::RealizeResource(
273 const CPDF_Object* pResource,
274 const ByteString& bsType) const {
275 DCHECK(pResource);
276 if (!m_pObjHolder->GetResources()) {
277 m_pObjHolder->SetResources(m_pDocument->NewIndirect<CPDF_Dictionary>());
278 m_pObjHolder->GetMutableDict()->SetNewFor<CPDF_Reference>(
279 pdfium::page_object::kResources, m_pDocument,
280 m_pObjHolder->GetResources()->GetObjNum());
281 }
282
283 RetainPtr<CPDF_Dictionary> pResList =
284 m_pObjHolder->GetMutableResources()->GetOrCreateDictFor(bsType);
285 ByteString name;
286 int idnum = 1;
287 while (true) {
288 name = ByteString::Format("FX%c%d", bsType[0], idnum);
289 if (!pResList->KeyExist(name))
290 break;
291
292 idnum++;
293 }
294 pResList->SetNewFor<CPDF_Reference>(name, m_pDocument,
295 pResource->GetObjNum());
296 return name;
297}
298
299bool CPDF_PageContentGenerator::ProcessPageObjects(fxcrt::ostringstream* buf) {
300 bool bDirty = false;
301 std::unique_ptr<const CPDF_ContentMarks> empty_content_marks =
302 std::make_unique<CPDF_ContentMarks>();
303 const CPDF_ContentMarks* content_marks = empty_content_marks.get();
304
305 for (auto& pPageObj : m_pageObjects) {
306 if (m_pObjHolder->IsPage() && !pPageObj->IsDirty())
307 continue;
308
309 bDirty = true;
310 content_marks = ProcessContentMarks(buf, pPageObj, content_marks);
311 ProcessPageObject(buf, pPageObj);
312 }
313 FinishMarks(buf, content_marks);
314 return bDirty;
315}
316
317void CPDF_PageContentGenerator::UpdateStreamlessPageObjects(
318 int new_content_stream_index) {
319 for (auto& pPageObj : m_pageObjects) {
320 if (pPageObj->GetContentStream() == CPDF_PageObject::kNoContentStream)
321 pPageObj->SetContentStream(new_content_stream_index);
322 }
323}
324
325const CPDF_ContentMarks* CPDF_PageContentGenerator::ProcessContentMarks(
326 fxcrt::ostringstream* buf,
327 const CPDF_PageObject* pPageObj,
328 const CPDF_ContentMarks* pPrev) {
329 const CPDF_ContentMarks* pNext = pPageObj->GetContentMarks();
330 const size_t first_different = pPrev->FindFirstDifference(pNext);
331
332 // Close all marks that are in prev but not in next.
333 // Technically we should iterate backwards to close from the top to the
334 // bottom, but since the EMC operators do not identify which mark they are
335 // closing, it does not matter.
336 for (size_t i = first_different; i < pPrev->CountItems(); ++i)
337 *buf << "EMC\n";
338
339 // Open all marks that are in next but not in prev.
340 for (size_t i = first_different; i < pNext->CountItems(); ++i) {
341 const CPDF_ContentMarkItem* item = pNext->GetItem(i);
342
343 // Write mark tag.
344 *buf << "/" << PDF_NameEncode(item->GetName()) << " ";
345
346 // If there are no parameters, write a BMC (begin marked content) operator.
347 if (item->GetParamType() == CPDF_ContentMarkItem::kNone) {
348 *buf << "BMC\n";
349 continue;
350 }
351
352 // If there are parameters, write properties, direct or indirect.
353 switch (item->GetParamType()) {
354 case CPDF_ContentMarkItem::kDirectDict: {
355 CPDF_StringArchiveStream archive_stream(buf);
356 item->GetParam()->WriteTo(&archive_stream, nullptr);
357 *buf << " ";
358 break;
359 }
360 case CPDF_ContentMarkItem::kPropertiesDict: {
361 *buf << "/" << item->GetPropertyName() << " ";
362 break;
363 }
364 case CPDF_ContentMarkItem::kNone:
365 NOTREACHED_NORETURN();
366 }
367
368 // Write BDC (begin dictionary content) operator.
369 *buf << "BDC\n";
370 }
371
372 return pNext;
373}
374
375void CPDF_PageContentGenerator::FinishMarks(
376 fxcrt::ostringstream* buf,
377 const CPDF_ContentMarks* pContentMarks) {
378 // Technically we should iterate backwards to close from the top to the
379 // bottom, but since the EMC operators do not identify which mark they are
380 // closing, it does not matter.
381 for (size_t i = 0; i < pContentMarks->CountItems(); ++i)
382 *buf << "EMC\n";
383}
384
385void CPDF_PageContentGenerator::ProcessPageObject(fxcrt::ostringstream* buf,
386 CPDF_PageObject* pPageObj) {
387 if (CPDF_ImageObject* pImageObject = pPageObj->AsImage())
388 ProcessImage(buf, pImageObject);
389 else if (CPDF_FormObject* pFormObj = pPageObj->AsForm())
390 ProcessForm(buf, pFormObj);
391 else if (CPDF_PathObject* pPathObj = pPageObj->AsPath())
392 ProcessPath(buf, pPathObj);
393 else if (CPDF_TextObject* pTextObj = pPageObj->AsText())
394 ProcessText(buf, pTextObj);
395 pPageObj->SetDirty(false);
396}
397
398void CPDF_PageContentGenerator::ProcessImage(fxcrt::ostringstream* buf,
399 CPDF_ImageObject* pImageObj) {
400 if ((pImageObj->matrix().a == 0 && pImageObj->matrix().b == 0) ||
401 (pImageObj->matrix().c == 0 && pImageObj->matrix().d == 0)) {
402 return;
403 }
404
405 RetainPtr<CPDF_Image> pImage = pImageObj->GetImage();
406 if (pImage->IsInline())
407 return;
408
409 RetainPtr<const CPDF_Stream> pStream = pImage->GetStream();
410 if (!pStream)
411 return;
412
413 *buf << "q ";
414 WriteMatrix(*buf, pImageObj->matrix()) << " cm ";
415
416 bool bWasInline = pStream->IsInline();
417 if (bWasInline)
418 pImage->ConvertStreamToIndirectObject();
419
420 ByteString name = RealizeResource(pStream, "XObject");
421 pImageObj->SetResourceName(name);
422
423 if (bWasInline) {
424 auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument);
425 pImageObj->SetImage(pPageData->GetImage(pStream->GetObjNum()));
426 }
427
428 *buf << "/" << PDF_NameEncode(name) << " Do Q\n";
429}
430
431void CPDF_PageContentGenerator::ProcessForm(fxcrt::ostringstream* buf,
432 CPDF_FormObject* pFormObj) {
433 if ((pFormObj->form_matrix().a == 0 && pFormObj->form_matrix().b == 0) ||
434 (pFormObj->form_matrix().c == 0 && pFormObj->form_matrix().d == 0)) {
435 return;
436 }
437
438 RetainPtr<const CPDF_Stream> pStream = pFormObj->form()->GetStream();
439 if (!pStream)
440 return;
441
442 ByteString name = RealizeResource(pStream.Get(), "XObject");
443 pFormObj->SetResourceName(name);
444
445 *buf << "q\n";
446 WriteMatrix(*buf, pFormObj->form_matrix()) << " cm ";
447 *buf << "/" << PDF_NameEncode(name) << " Do Q\n";
448}
449
450// Processing path construction with operators from Table 4.9 of PDF spec 1.7:
451// "re" appends a rectangle (here, used only if the whole path is a rectangle)
452// "m" moves current point to the given coordinates
453// "l" creates a line from current point to the new point
454// "c" adds a Bezier curve from current to last point, using the two other
455// points as the Bezier control points
456// Note: "l", "c" change the current point
457// "h" closes the subpath (appends a line from current to starting point)
458void CPDF_PageContentGenerator::ProcessPathPoints(fxcrt::ostringstream* buf,
459 CPDF_Path* pPath) {
460 pdfium::span<const CFX_Path::Point> points = pPath->GetPoints();
461 if (pPath->IsRect()) {
462 CFX_PointF diff = points[2].m_Point - points[0].m_Point;
463 WritePoint(*buf, points[0].m_Point) << " ";
464 WritePoint(*buf, diff) << " re";
465 return;
466 }
467 for (size_t i = 0; i < points.size(); ++i) {
468 if (i > 0)
469 *buf << " ";
470
471 WritePoint(*buf, points[i].m_Point);
472
473 CFX_Path::Point::Type point_type = points[i].m_Type;
474 if (point_type == CFX_Path::Point::Type::kMove) {
475 *buf << " m";
476 } else if (point_type == CFX_Path::Point::Type::kLine) {
477 *buf << " l";
478 } else if (point_type == CFX_Path::Point::Type::kBezier) {
479 if (i + 2 >= points.size() ||
480 !points[i].IsTypeAndOpen(CFX_Path::Point::Type::kBezier) ||
481 !points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kBezier) ||
482 points[i + 2].m_Type != CFX_Path::Point::Type::kBezier) {
483 // If format is not supported, close the path and paint
484 *buf << " h";
485 break;
486 }
487 *buf << " ";
488 WritePoint(*buf, points[i + 1].m_Point) << " ";
489 WritePoint(*buf, points[i + 2].m_Point) << " c";
490 i += 2;
491 }
492 if (points[i].m_CloseFigure)
493 *buf << " h";
494 }
495}
496
497// Processing path painting with operators from Table 4.10 of PDF spec 1.7:
498// Path painting operators: "S", "n", "B", "f", "B*", "f*", depending on
499// the filling mode and whether we want stroking the path or not.
500// "Q" restores the graphics state imposed by the ProcessGraphics method.
501void CPDF_PageContentGenerator::ProcessPath(fxcrt::ostringstream* buf,
502 CPDF_PathObject* pPathObj) {
503 ProcessGraphics(buf, pPathObj);
504
505 WriteMatrix(*buf, pPathObj->matrix()) << " cm ";
506 ProcessPathPoints(buf, &pPathObj->path());
507
508 if (pPathObj->has_no_filltype())
509 *buf << (pPathObj->stroke() ? " S" : " n");
510 else if (pPathObj->has_winding_filltype())
511 *buf << (pPathObj->stroke() ? " B" : " f");
512 else if (pPathObj->has_alternate_filltype())
513 *buf << (pPathObj->stroke() ? " B*" : " f*");
514 *buf << " Q\n";
515}
516
517// This method supports color operators rg and RGB from Table 4.24 of PDF spec
518// 1.7. A color will not be set if the colorspace is not DefaultRGB or the RGB
519// values cannot be obtained. The method also adds an external graphics
520// dictionary, as described in Section 4.3.4.
521// "rg" sets the fill color, "RG" sets the stroke color (using DefaultRGB)
522// "w" sets the stroke line width.
523// "ca" sets the fill alpha, "CA" sets the stroke alpha.
524// "W" and "W*" modify the clipping path using the nonzero winding rule and
525// even-odd rules, respectively.
526// "q" saves the graphics state, so that the settings can later be reversed
527void CPDF_PageContentGenerator::ProcessGraphics(fxcrt::ostringstream* buf,
528 CPDF_PageObject* pPageObj) {
529 *buf << "q ";
530 float fillColor[3];
531 if (GetColor(pPageObj->color_state().GetFillColor(), fillColor)) {
532 *buf << fillColor[0] << " " << fillColor[1] << " " << fillColor[2]
533 << " rg ";
534 }
535 float strokeColor[3];
536 if (GetColor(pPageObj->color_state().GetStrokeColor(), strokeColor)) {
537 *buf << strokeColor[0] << " " << strokeColor[1] << " " << strokeColor[2]
538 << " RG ";
539 }
540 float lineWidth = pPageObj->graph_state().GetLineWidth();
541 if (lineWidth != 1.0f)
542 WriteFloat(*buf, lineWidth) << " w ";
545 *buf << static_cast<int>(lineCap) << " J ";
548 *buf << static_cast<int>(lineJoin) << " j ";
549 std::vector<float> dash_array = pPageObj->graph_state().GetLineDashArray();
550 if (dash_array.size()) {
551 *buf << "[";
552 for (size_t i = 0; i < dash_array.size(); ++i) {
553 if (i > 0) {
554 *buf << " ";
555 }
556 WriteFloat(*buf, dash_array[i]);
557 }
558 *buf << "] ";
559 WriteFloat(*buf, pPageObj->graph_state().GetLineDashPhase()) << " d ";
560 }
561
562 const CPDF_ClipPath& clip_path = pPageObj->clip_path();
563 if (clip_path.HasRef()) {
564 for (size_t i = 0; i < clip_path.GetPathCount(); ++i) {
565 CPDF_Path path = clip_path.GetPath(i);
566 ProcessPathPoints(buf, &path);
567 switch (clip_path.GetClipType(i)) {
569 *buf << " W ";
570 break;
572 *buf << " W* ";
573 break;
575 NOTREACHED_NORETURN();
576 }
577
578 // Use a no-op path-painting operator to terminate the path without
579 // causing any marks to be placed on the page.
580 *buf << "n ";
581 }
582 }
583
584 GraphicsData graphD;
588 if (graphD.fillAlpha == 1.0f && graphD.strokeAlpha == 1.0f &&
590 return;
591 }
592
593 ByteString name;
594 absl::optional<ByteString> maybe_name =
595 m_pObjHolder->GraphicsMapSearch(graphD);
596 if (maybe_name.has_value()) {
597 name = std::move(maybe_name.value());
598 } else {
599 auto gsDict = pdfium::MakeRetain<CPDF_Dictionary>();
600 if (graphD.fillAlpha != 1.0f)
601 gsDict->SetNewFor<CPDF_Number>("ca", graphD.fillAlpha);
602
603 if (graphD.strokeAlpha != 1.0f)
604 gsDict->SetNewFor<CPDF_Number>("CA", graphD.strokeAlpha);
605
606 if (graphD.blendType != BlendMode::kNormal) {
607 gsDict->SetNewFor<CPDF_Name>("BM",
609 }
610 m_pDocument->AddIndirectObject(gsDict);
611 name = RealizeResource(std::move(gsDict), "ExtGState");
612 pPageObj->mutable_general_state().SetGraphicsResourceNames({name});
613 m_pObjHolder->GraphicsMapInsert(graphD, name);
614 }
615 *buf << "/" << PDF_NameEncode(name) << " gs ";
616}
617
618void CPDF_PageContentGenerator::ProcessDefaultGraphics(
619 fxcrt::ostringstream* buf) {
620 *buf << "0 0 0 RG 0 0 0 rg 1 w "
621 << static_cast<int>(CFX_GraphStateData::LineCap::kButt) << " J "
622 << static_cast<int>(CFX_GraphStateData::LineJoin::kMiter) << " j\n";
623 m_DefaultGraphicsName = GetOrCreateDefaultGraphics();
624 *buf << "/" << PDF_NameEncode(m_DefaultGraphicsName) << " gs ";
625}
626
627ByteString CPDF_PageContentGenerator::GetOrCreateDefaultGraphics() const {
628 GraphicsData defaultGraphics;
629 defaultGraphics.fillAlpha = 1.0f;
630 defaultGraphics.strokeAlpha = 1.0f;
631 defaultGraphics.blendType = BlendMode::kNormal;
632
633 absl::optional<ByteString> maybe_name =
634 m_pObjHolder->GraphicsMapSearch(defaultGraphics);
635 if (maybe_name.has_value())
636 return maybe_name.value();
637
638 auto gsDict = pdfium::MakeRetain<CPDF_Dictionary>();
639 gsDict->SetNewFor<CPDF_Number>("ca", defaultGraphics.fillAlpha);
640 gsDict->SetNewFor<CPDF_Number>("CA", defaultGraphics.strokeAlpha);
641 gsDict->SetNewFor<CPDF_Name>("BM", "Normal");
642 m_pDocument->AddIndirectObject(gsDict);
643 ByteString name = RealizeResource(std::move(gsDict), "ExtGState");
644 m_pObjHolder->GraphicsMapInsert(defaultGraphics, name);
645 return name;
646}
647
648// This method adds text to the buffer, BT begins the text object, ET ends it.
649// Tm sets the text matrix (allows positioning and transforming text).
650// Tf sets the font name (from Font in Resources) and font size.
651// Tr sets the text rendering mode.
652// Tj sets the actual text, <####...> is used when specifying charcodes.
653void CPDF_PageContentGenerator::ProcessText(fxcrt::ostringstream* buf,
654 CPDF_TextObject* pTextObj) {
655 ProcessGraphics(buf, pTextObj);
656 *buf << "BT ";
657 WriteMatrix(*buf, pTextObj->GetTextMatrix()) << " Tm ";
658 RetainPtr<CPDF_Font> pFont(pTextObj->GetFont());
659 if (!pFont)
660 pFont = CPDF_Font::GetStockFont(m_pDocument, "Helvetica");
661
662 FontData data;
663 const CPDF_FontEncoding* pEncoding = nullptr;
664 if (pFont->IsType1Font()) {
665 data.type = "Type1";
666 pEncoding = pFont->AsType1Font()->GetEncoding();
667 } else if (pFont->IsTrueTypeFont()) {
668 data.type = "TrueType";
669 pEncoding = pFont->AsTrueTypeFont()->GetEncoding();
670 } else if (pFont->IsCIDFont()) {
671 data.type = "Type0";
672 } else {
673 return;
674 }
675 data.baseFont = pFont->GetBaseFontName();
676
677 ByteString dict_name;
678 absl::optional<ByteString> maybe_name = m_pObjHolder->FontsMapSearch(data);
679 if (maybe_name.has_value()) {
680 dict_name = std::move(maybe_name.value());
681 } else {
682 RetainPtr<const CPDF_Object> pIndirectFont = pFont->GetFontDict();
683 if (pIndirectFont->IsInline()) {
684 // In this case we assume it must be a standard font
685 auto pFontDict = pdfium::MakeRetain<CPDF_Dictionary>();
686 pFontDict->SetNewFor<CPDF_Name>("Type", "Font");
687 pFontDict->SetNewFor<CPDF_Name>("Subtype", data.type);
688 pFontDict->SetNewFor<CPDF_Name>("BaseFont", data.baseFont);
689 if (pEncoding) {
690 pFontDict->SetFor("Encoding",
691 pEncoding->Realize(m_pDocument->GetByteStringPool()));
692 }
693 m_pDocument->AddIndirectObject(pFontDict);
694 pIndirectFont = std::move(pFontDict);
695 }
696 dict_name = RealizeResource(std::move(pIndirectFont), "Font");
697 m_pObjHolder->FontsMapInsert(data, dict_name);
698 }
699 pTextObj->SetResourceName(dict_name);
700
701 *buf << "/" << PDF_NameEncode(dict_name) << " ";
702 WriteFloat(*buf, pTextObj->GetFontSize()) << " Tf ";
703 *buf << static_cast<int>(pTextObj->GetTextRenderMode()) << " Tr ";
704 ByteString text;
705 for (uint32_t charcode : pTextObj->GetCharCodes()) {
706 if (charcode != CPDF_Font::kInvalidCharCode)
707 pFont->AppendChar(&text, charcode);
708 }
709 *buf << PDF_HexEncodeString(text.AsStringView()) << " Tj ET";
710 *buf << " Q\n";
711}
float GetLineDashPhase() const
float GetLineWidth() const
CFX_GraphStateData::LineCap GetLineCap() const
CFX_GraphStateData::LineJoin GetLineJoin() const
bool HasRef() const
const CPDF_Color * GetStrokeColor() const
const CPDF_Color * GetFillColor() const
bool GetRGB(int *R, int *G, int *B) const
bool IsColorSpaceRGB() const
const ByteString & GetName() const
const ByteString & GetPropertyName() const
ParamType GetParamType() const
const CFX_Matrix & form_matrix() const
float GetStrokeAlpha() const
BlendMode GetBlendType() const
ByteString GetBlendMode() const
void SetImage(RetainPtr< CPDF_Image > pImage)
const CFX_Matrix & matrix() const
bool ProcessPageObjects(fxcrt::ostringstream *buf)
CPDF_PageContentGenerator(CPDF_PageObjectHolder *pObjHolder)
const CPDF_ColorState & color_state() const
void SetDirty(bool value)
const CPDF_GeneralState & general_state() const
const ByteString & GetResourceName() const
const CPDF_ClipPath & clip_path() const
virtual Type GetType() const =0
CPDF_GeneralState & mutable_general_state()
const CPDF_ContentMarks * GetContentMarks() const
void SetResourceName(const ByteString &resource_name)
const CFX_GraphState & graph_state() const
bool has_winding_filltype() const
bool stroke() const
const CFX_Matrix & matrix() const
bool has_alternate_filltype() const
bool has_no_filltype() const
bool IsRect() const
Definition cpdf_path.cpp:37
CFX_Matrix GetTextMatrix() const
float GetFontSize() const
TextRenderingMode GetTextRenderMode() const
static ByteString Format(const char *pFormat,...)
ByteString & operator=(ByteString &&that) noexcept
bool IsEmpty() const
Definition bytestring.h:119
CharType operator[](const size_t index) const
Definition bytestring.h:150
ByteString PDF_NameEncode(const ByteString &orig)
BlendMode
Definition fx_dib.h:49
#define CHECK(cvref)