7#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
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"
52using ResourcesMap = std::map<ByteString, std::set<ByteString>>;
54bool GetColor(
const CPDF_Color* pColor,
float* rgb) {
60 rgb[0] = intRGB[0] / 255.0f;
61 rgb[1] = intRGB[1] / 255.0f;
62 rgb[2] = intRGB[2] / 255.0f;
67 ResourcesMap& seen_resources) {
72 seen_resources[
"Font"].insert(resource_name);
76 seen_resources[
"XObject"].insert(resource_name);
84 for (
const auto& name : page_object->GetGraphicsResourceNames()) {
85 CHECK(!name.IsEmpty());
86 seen_resources[
"ExtGState"].insert(name);
90void RemoveUnusedResources(
RetainPtr<CPDF_Dictionary> resources_dict,
91 const ResourcesMap& resources_in_use) {
96 static constexpr const char* kResourceKeys[] = {
"ExtGState",
"Font",
98 for (
const char* resource_key : kResourceKeys) {
99 RetainPtr<CPDF_Dictionary> resource_dict =
100 resources_dict->GetMutableDictFor(resource_key);
101 if (!resource_dict) {
105 std::vector<ByteString> keys;
108 for (
auto& it : resource_dict_locker) {
109 keys.push_back(it.first);
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)) {
122 resource_dict->RemoveFor(key.AsStringView());
132 for (
const auto& pObj : *pObjHolder) {
134 m_pageObjects.emplace_back(pObj.get());
141 DCHECK(m_pObjHolder->IsPage());
142 std::map<int32_t, fxcrt::ostringstream> new_stream_data =
143 GenerateModifiedStreams();
145 if (new_stream_data.empty()) {
149 UpdateContentStreams(std::move(new_stream_data));
150 UpdateResourcesDict();
153std::map<int32_t, fxcrt::ostringstream>
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());
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());
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;
172 for (int32_t dirty_stream : all_dirty_streams) {
173 fxcrt::ostringstream buf;
177 if (!m_pObjHolder->GetLastCTM().IsIdentity())
178 WriteMatrix(buf, m_pObjHolder->GetLastCTM().GetInverse()) <<
" cm\n";
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();
187 for (
auto& pPageObj : m_pageObjects) {
188 int stream_index = pPageObj->GetContentStream();
189 auto it = streams.find(stream_index);
190 if (it == streams.end())
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);
201 for (int32_t dirty_stream : all_dirty_streams) {
202 fxcrt::ostringstream* buf = &streams[dirty_stream];
203 if (pdfium::Contains(empty_streams, dirty_stream)) {
207 FinishMarks(buf, current_content_marks[dirty_stream]);
218 std::map<int32_t, fxcrt::ostringstream>&& new_stream_data) {
219 CHECK(!new_stream_data.empty());
222 m_DefaultGraphicsName = GetOrCreateDefaultGraphics();
225 for (
auto& pair : new_stream_data) {
226 int32_t stream_index = pair.first;
227 fxcrt::ostringstream* buf = &pair.second;
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);
236 page_content_manager.UpdateStream(stream_index, buf);
241 RetainPtr<CPDF_Dictionary> resources = m_pObjHolder->GetMutableResources();
246 const uint32_t resources_object_number = resources->GetObjNum();
247 if (resources_object_number) {
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);
261 ResourcesMap seen_resources;
262 for (
auto& page_object : m_pageObjects) {
263 RecordPageObjectResourceUsage(page_object, seen_resources);
265 if (!m_DefaultGraphicsName.IsEmpty()) {
266 seen_resources[
"ExtGState"].insert(m_DefaultGraphicsName);
269 RemoveUnusedResources(
std::move(resources), seen_resources);
274 const ByteString& bsType)
const {
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());
284 m_pObjHolder->GetMutableResources()->GetOrCreateDictFor(bsType);
289 if (!pResList->KeyExist(name))
294 pResList->SetNewFor<CPDF_Reference>(name, m_pDocument,
295 pResource->GetObjNum());
301 std::unique_ptr<
const CPDF_ContentMarks> empty_content_marks =
302 std::make_unique<CPDF_ContentMarks>();
305 for (
auto& pPageObj : m_pageObjects) {
306 if (m_pObjHolder->IsPage() && !pPageObj->IsDirty())
310 content_marks = ProcessContentMarks(buf, pPageObj, content_marks);
311 ProcessPageObject(buf, pPageObj);
313 FinishMarks(buf, content_marks);
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);
326 fxcrt::ostringstream* buf,
330 const size_t first_different = pPrev->FindFirstDifference(pNext);
336 for (size_t i = first_different; i < pPrev->CountItems(); ++i)
340 for (size_t i = first_different; i < pNext->CountItems(); ++i) {
341 const CPDF_ContentMarkItem* item = pNext->GetItem(i);
355 CPDF_StringArchiveStream archive_stream(buf);
356 item->GetParam()->WriteTo(&archive_stream,
nullptr);
364 case CPDF_ContentMarkItem
::kNone:
365 NOTREACHED_NORETURN();
376 fxcrt::ostringstream* buf,
381 for (size_t i = 0; i < pContentMarks->CountItems(); ++i)
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);
399 CPDF_ImageObject* pImageObj) {
405 RetainPtr<CPDF_Image> pImage = pImageObj->GetImage();
406 if (pImage->IsInline())
409 RetainPtr<
const CPDF_Stream> pStream = pImage->GetStream();
414 WriteMatrix(*buf, pImageObj
->matrix()) <<
" cm ";
416 bool bWasInline = pStream->IsInline();
418 pImage->ConvertStreamToIndirectObject();
420 ByteString name = RealizeResource(pStream,
"XObject");
424 auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument);
425 pImageObj
->SetImage(pPageData->GetImage(pStream->GetObjNum())
);
432 CPDF_FormObject* pFormObj) {
438 RetainPtr<
const CPDF_Stream> pStream = pFormObj->form()->GetStream();
442 ByteString name = RealizeResource(pStream.Get(),
"XObject");
460 pdfium::span<
const CFX_Path::
Point> points = pPath->GetPoints();
462 CFX_PointF diff = points[2].m_Point - points[0].m_Point;
463 WritePoint(*buf, points[0].m_Point) <<
" ";
464 WritePoint(*buf, diff) <<
" re";
467 for (size_t i = 0; i < points.size(); ++i) {
471 WritePoint(*buf, points[i].m_Point);
479 if (i + 2 >= points.size() ||
488 WritePoint(*buf, points[i + 1].m_Point) <<
" ";
489 WritePoint(*buf, points[i + 2].m_Point) <<
" c";
492 if (points[i].m_CloseFigure)
502 CPDF_PathObject* pPathObj) {
503 ProcessGraphics(buf, pPathObj);
505 WriteMatrix(*buf, pPathObj
->matrix()) <<
" cm ";
506 ProcessPathPoints(buf, &pPathObj->path());
532 *buf << fillColor[0] <<
" " << fillColor[1] <<
" " << fillColor[2]
535 float strokeColor[3];
537 *buf << strokeColor[0] <<
" " << strokeColor[1] <<
" " << strokeColor[2]
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()) {
552 for (size_t i = 0; i < dash_array.size(); ++i) {
556 WriteFloat(*buf, dash_array[i]);
564 for (size_t i = 0; i < clip_path.GetPathCount(); ++i) {
566 ProcessPathPoints(buf, &path);
567 switch (clip_path.GetClipType(i)) {
575 NOTREACHED_NORETURN();
594 absl::optional<ByteString> maybe_name =
595 m_pObjHolder->GraphicsMapSearch(graphD);
596 if (maybe_name.has_value()) {
597 name = std::move(maybe_name.value());
599 auto gsDict = pdfium::MakeRetain<CPDF_Dictionary>();
601 gsDict->SetNewFor<CPDF_Number>(
"ca", graphD
.fillAlpha);
604 gsDict->SetNewFor<CPDF_Number>(
"CA", graphD
.strokeAlpha);
607 gsDict->SetNewFor<CPDF_Name>(
"BM",
610 m_pDocument->AddIndirectObject(gsDict);
611 name = RealizeResource(
std::move(gsDict),
"ExtGState");
613 m_pObjHolder->GraphicsMapInsert(graphD, name);
619 fxcrt::ostringstream* buf) {
620 *buf <<
"0 0 0 RG 0 0 0 rg 1 w "
623 m_DefaultGraphicsName = GetOrCreateDefaultGraphics();
624 *buf <<
"/" << PDF_NameEncode(m_DefaultGraphicsName) <<
" gs ";
633 absl::optional<ByteString> maybe_name =
634 m_pObjHolder->GraphicsMapSearch(defaultGraphics);
635 if (maybe_name.has_value())
636 return maybe_name.value();
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);
654 CPDF_TextObject* pTextObj) {
655 ProcessGraphics(buf, pTextObj);
660 pFont = CPDF_Font::GetStockFont(m_pDocument,
"Helvetica");
664 if (pFont->IsType1Font()) {
666 pEncoding = pFont->AsType1Font()->GetEncoding();
667 }
else if (pFont->IsTrueTypeFont()) {
668 data.type =
"TrueType";
669 pEncoding = pFont->AsTrueTypeFont()->GetEncoding();
670 }
else if (pFont->IsCIDFont()) {
675 data.baseFont = pFont->GetBaseFontName();
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());
683 if (pIndirectFont->IsInline()) {
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);
690 pFontDict->SetFor(
"Encoding",
691 pEncoding->Realize(m_pDocument->GetByteStringPool()));
693 m_pDocument->AddIndirectObject(pFontDict);
694 pIndirectFont =
std::move(pFontDict);
696 dict_name = RealizeResource(
std::move(pIndirectFont),
"Font");
697 m_pObjHolder->FontsMapInsert(data, dict_name);
705 for (uint32_t charcode : pTextObj->GetCharCodes()) {
706 if (charcode != CPDF_Font::kInvalidCharCode)
707 pFont->AppendChar(&text, charcode);
709 *buf << PDF_HexEncodeString(text.AsStringView()) <<
" Tj ET";
float GetLineDashPhase() const
float GetLineWidth() const
CFX_GraphStateData::LineCap GetLineCap() const
CFX_GraphStateData::LineJoin GetLineJoin() 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
float GetStrokeAlpha() const
float GetFillAlpha() 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)
~CPDF_PageContentGenerator()
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
const CFX_Matrix & matrix() const
bool has_alternate_filltype() const
bool has_no_filltype() const
CFX_Matrix GetTextMatrix() const
float GetFontSize() const
TextRenderingMode GetTextRenderMode() const
static ByteString Format(const char *pFormat,...)
ByteString & operator=(ByteString &&that) noexcept
CharType operator[](const size_t index) const
ByteString PDF_NameEncode(const ByteString &orig)