7#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
17#include "constants/page_object.h"
18#include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
19#include "core/fpdfapi/edit/cpdf_pagecontentmanager.h"
20#include "core/fpdfapi/edit/cpdf_stringarchivestream.h"
21#include "core/fpdfapi/font/cpdf_truetypefont.h"
22#include "core/fpdfapi/font/cpdf_type1font.h"
23#include "core/fpdfapi/page/cpdf_contentmarks.h"
24#include "core/fpdfapi/page/cpdf_docpagedata.h"
25#include "core/fpdfapi/page/cpdf_form.h"
26#include "core/fpdfapi/page/cpdf_formobject.h"
27#include "core/fpdfapi/page/cpdf_image.h"
28#include "core/fpdfapi/page/cpdf_imageobject.h"
29#include "core/fpdfapi/page/cpdf_page.h"
30#include "core/fpdfapi/page/cpdf_path.h"
31#include "core/fpdfapi/page/cpdf_pathobject.h"
32#include "core/fpdfapi/page/cpdf_textobject.h"
33#include "core/fpdfapi/parser/cpdf_array.h"
34#include "core/fpdfapi/parser/cpdf_dictionary.h"
35#include "core/fpdfapi/parser/cpdf_document.h"
36#include "core/fpdfapi/parser/cpdf_name.h"
37#include "core/fpdfapi/parser/cpdf_number.h"
38#include "core/fpdfapi/parser/cpdf_reference.h"
39#include "core/fpdfapi/parser/cpdf_stream.h"
40#include "core/fpdfapi/parser/fpdf_parser_decode.h"
41#include "core/fpdfapi/parser/fpdf_parser_utility.h"
42#include "core/fpdfapi/parser/object_tree_traversal_util.h"
43#include "core/fxcrt/check.h"
44#include "core/fxcrt/check_op.h"
45#include "core/fxcrt/containers/contains.h"
46#include "core/fxcrt/notreached.h"
47#include "core/fxcrt/numerics/safe_conversions.h"
48#include "core/fxcrt/span.h"
54using ResourcesMap = std::map<ByteString, std::set<ByteString>>;
57bool WriteColorToStream(fxcrt::ostringstream& buf,
const CPDF_Color* color) {
63 if (!colors.has_value()) {
67 WriteFloat(buf, colors.value().red) <<
" ";
68 WriteFloat(buf, colors.value().green) <<
" ";
69 WriteFloat(buf, colors.value().blue);
74 ResourcesMap& seen_resources) {
76 if (!resource_name.IsEmpty()) {
79 seen_resources[
"Font"].insert(resource_name);
83 seen_resources[
"XObject"].insert(resource_name);
91 for (
const auto& name : page_object->GetGraphicsResourceNames()) {
92 CHECK(!name.IsEmpty());
93 seen_resources[
"ExtGState"].insert(name);
98 const ResourcesMap& resources_in_use) {
103 static constexpr const char* kResourceKeys[] = {
"ExtGState",
"Font",
105 for (
const char* resource_key : kResourceKeys) {
107 resources_dict->GetMutableDictFor(resource_key);
108 if (!resource_dict) {
112 std::vector<ByteString> keys;
115 for (
auto& it : resource_dict_locker) {
116 keys.push_back(it.first);
120 auto it = resources_in_use.find(resource_key);
121 const std::set<ByteString>* resource_in_use_of_current_type =
122 it != resources_in_use.end() ? &it->second :
nullptr;
123 for (
const ByteString& key : keys) {
124 if (resource_in_use_of_current_type &&
125 pdfium::Contains(*resource_in_use_of_current_type, key)) {
129 resource_dict->RemoveFor(key.AsStringView());
139 for (
const auto& pObj : *pObjHolder) {
140 m_pageObjects.emplace_back(pObj.get());
147 DCHECK(m_pObjHolder->IsPage());
148 std::map<int32_t, fxcrt::ostringstream> new_stream_data =
149 GenerateModifiedStreams();
151 if (new_stream_data.empty()) {
155 UpdateContentStreams(std::move(new_stream_data));
156 UpdateResourcesDict();
159std::map<int32_t, fxcrt::ostringstream>
162 std::set<int32_t> all_dirty_streams;
163 for (
auto& pPageObj : m_pageObjects) {
164 if (pPageObj->IsDirty())
165 all_dirty_streams.insert(pPageObj->GetContentStream());
167 std::set<int32_t> marked_dirty_streams = m_pObjHolder->TakeDirtyStreams();
168 all_dirty_streams.insert(marked_dirty_streams.begin(),
169 marked_dirty_streams.end());
172 std::map<int32_t, fxcrt::ostringstream> streams;
173 std::set<int32_t> empty_streams;
174 std::unique_ptr<
const CPDF_ContentMarks> empty_content_marks =
176 std::map<int32_t,
const CPDF_ContentMarks*> current_content_marks;
178 for (int32_t dirty_stream : all_dirty_streams) {
179 fxcrt::ostringstream buf;
184 const CFX_Matrix ctm =
185 m_pObjHolder->GetCTMAtBeginningOfStream(dirty_stream);
186 if (!ctm.IsIdentity()) {
187 WriteMatrix(buf, ctm.GetInverse()) <<
" cm\n";
190 ProcessDefaultGraphics(&buf);
191 streams[dirty_stream] = std::move(buf);
192 empty_streams.insert(dirty_stream);
193 current_content_marks[dirty_stream] = empty_content_marks.get();
197 for (
auto& pPageObj : m_pageObjects) {
198 int stream_index = pPageObj->GetContentStream();
199 auto it = streams.find(stream_index);
200 if (it == streams.end())
203 fxcrt::ostringstream* buf = &it->second;
204 empty_streams.erase(stream_index);
205 current_content_marks[stream_index] =
206 ProcessContentMarks(buf, pPageObj, current_content_marks[stream_index]);
207 ProcessPageObject(buf, pPageObj);
211 for (int32_t dirty_stream : all_dirty_streams) {
215 if (dirty_stream == 0) {
217 ctm = m_pObjHolder->GetCTMAtEndOfStream(dirty_stream);
218 affects_ctm = !ctm.IsIdentity();
219 }
else if (dirty_stream > 0) {
220 prev_ctm = m_pObjHolder->GetCTMAtEndOfStream(dirty_stream - 1);
221 ctm = m_pObjHolder->GetCTMAtEndOfStream(dirty_stream);
222 affects_ctm = prev_ctm != ctm;
224 CHECK_EQ(CPDF_PageObject::kNoContentStream, dirty_stream);
230 const bool is_empty = pdfium::Contains(empty_streams, dirty_stream);
232 fxcrt::ostringstream* buf = &streams[dirty_stream];
233 if (is_empty && !affects_ctm) {
240 FinishMarks(buf, current_content_marks[dirty_stream]);
248 CFX_Matrix ctm_difference = prev_ctm.GetInverse() * ctm;
249 if (!ctm_difference.IsIdentity()) {
250 WriteMatrix(*buf, ctm_difference) <<
" cm\n";
259 std::map<int32_t, fxcrt::ostringstream>&& new_stream_data) {
260 CHECK(!new_stream_data.empty());
263 m_DefaultGraphicsName = GetOrCreateDefaultGraphics();
266 for (
auto& pair : new_stream_data) {
267 int32_t stream_index = pair.first;
268 fxcrt::ostringstream* buf = &pair.second;
270 if (stream_index == CPDF_PageObject::kNoContentStream) {
271 int new_stream_index =
272 pdfium::checked_cast<
int>(page_content_manager.AddStream(buf));
273 UpdateStreamlessPageObjects(new_stream_index);
277 page_content_manager.UpdateStream(stream_index, buf);
287 const uint32_t resources_object_number = resources->GetObjNum();
288 if (resources_object_number) {
291 if (pdfium::Contains(GetObjectsWithMultipleReferences(m_pDocument),
292 resources_object_number)) {
293 resources =
pdfium::WrapRetain(resources->Clone()->AsMutableDictionary());
294 const uint32_t clone_object_number =
295 m_pDocument->AddIndirectObject(resources);
296 m_pObjHolder->SetResources(resources);
297 m_pObjHolder->GetMutableDict()->SetNewFor<CPDF_Reference>(
298 pdfium::page_object::kResources, m_pDocument, clone_object_number);
302 ResourcesMap seen_resources;
303 for (
auto& page_object : m_pageObjects) {
304 RecordPageObjectResourceUsage(page_object, seen_resources);
306 if (!m_DefaultGraphicsName.IsEmpty()) {
307 seen_resources[
"ExtGState"].insert(m_DefaultGraphicsName);
310 RemoveUnusedResources(
std::move(resources), seen_resources);
317 if (!m_pObjHolder->GetResources()) {
318 m_pObjHolder->SetResources(m_pDocument->NewIndirect<CPDF_Dictionary>());
319 m_pObjHolder->GetMutableDict()->SetNewFor<CPDF_Reference>(
320 pdfium::page_object::kResources, m_pDocument,
321 m_pObjHolder->GetResources()->GetObjNum());
325 m_pObjHolder->GetMutableResources()->GetOrCreateDictFor(bsType);
329 name = ByteString::Format(
"FX%c%d", bsType[0], idnum);
330 if (!pResList->KeyExist(name))
335 pResList->SetNewFor<CPDF_Reference>(name, m_pDocument,
336 pResource->GetObjNum());
342 std::unique_ptr<
const CPDF_ContentMarks> empty_content_marks =
346 for (
auto& pPageObj : m_pageObjects) {
347 if (m_pObjHolder->IsPage() && !pPageObj->IsDirty())
351 content_marks = ProcessContentMarks(buf, pPageObj, content_marks);
352 ProcessPageObject(buf, pPageObj);
354 FinishMarks(buf, content_marks);
359 int new_content_stream_index) {
360 for (
auto& pPageObj : m_pageObjects) {
361 if (pPageObj->GetContentStream() == CPDF_PageObject::kNoContentStream)
362 pPageObj->SetContentStream(new_content_stream_index);
367 fxcrt::ostringstream* buf,
371 const size_t first_different = pPrev->FindFirstDifference(pNext);
377 for (size_t i = first_different; i < pPrev->CountItems(); ++i)
381 for (size_t i = first_different; i < pNext->CountItems(); ++i) {
382 const CPDF_ContentMarkItem* item = pNext->GetItem(i);
396 CPDF_StringArchiveStream archive_stream(buf);
397 item->GetParam()->WriteTo(&archive_stream,
nullptr);
405 case CPDF_ContentMarkItem
::kNone:
417 fxcrt::ostringstream* buf,
422 for (size_t i = 0; i < pContentMarks->CountItems(); ++i)
428 if (CPDF_ImageObject* pImageObject = pPageObj->AsImage())
429 ProcessImage(buf, pImageObject);
430 else if (CPDF_FormObject* pFormObj = pPageObj->AsForm())
431 ProcessForm(buf, pFormObj);
432 else if (CPDF_PathObject* pPathObj = pPageObj->AsPath())
433 ProcessPath(buf, pPathObj);
434 else if (CPDF_TextObject* pTextObj = pPageObj->AsText())
435 ProcessText(buf, pTextObj);
440 CPDF_ImageObject* pImageObj) {
442 if ((matrix
.a == 0 && matrix
.b == 0) || (matrix
.c == 0 && matrix
.d == 0)) {
446 RetainPtr<CPDF_Image> pImage = pImageObj->GetImage();
447 if (pImage->IsInline())
450 RetainPtr<
const CPDF_Stream> pStream = pImage->GetStream();
457 WriteMatrix(*buf, matrix) <<
" cm ";
460 bool bWasInline = pStream->IsInline();
462 pImage->ConvertStreamToIndirectObject();
464 ByteString name = RealizeResource(pStream,
"XObject");
468 auto* pPageData = CPDF_DocPageData::FromDocument(m_pDocument);
469 pImageObj
->SetImage(pPageData->GetImage(pStream->GetObjNum())
);
476 CPDF_FormObject* pFormObj) {
478 if ((matrix
.a == 0 && matrix
.b == 0) || (matrix
.c == 0 && matrix
.d == 0)) {
482 RetainPtr<
const CPDF_Stream> pStream = pFormObj->form()->GetStream();
486 ByteString name = RealizeResource(pStream.Get(),
"XObject");
492 WriteMatrix(*buf, matrix) <<
" cm ";
508 pdfium::span<
const CFX_Path::Point> points = pPath->GetPoints();
510 CFX_PointF diff = points[2].m_Point - points[0].m_Point;
511 WritePoint(*buf, points[0].m_Point) <<
" ";
512 WritePoint(*buf, diff) <<
" re";
515 for (size_t i = 0; i < points.size(); ++i) {
519 WritePoint(*buf, points[i].m_Point);
527 if (i + 2 >= points.size() ||
536 WritePoint(*buf, points[i + 1].m_Point) <<
" ";
537 WritePoint(*buf, points[i + 2].m_Point) <<
" c";
540 if (points[i].m_CloseFigure)
550 CPDF_PathObject* pPathObj) {
551 ProcessGraphics(buf, pPathObj);
555 WriteMatrix(*buf, matrix) <<
" cm ";
558 ProcessPathPoints(buf, &pPathObj->path());
589 if (line_width != 1.0f) {
590 WriteFloat(*buf, line_width) <<
" w ";
594 *buf <<
static_cast<
int>(lineCap) <<
" J ";
597 *buf <<
static_cast<
int>(lineJoin) <<
" j ";
598 std::vector<
float> dash_array = pPageObj
->graph_state().GetLineDashArray();
599 if (dash_array.size()) {
601 for (size_t i = 0; i < dash_array.size(); ++i) {
605 WriteFloat(*buf, dash_array[i]);
613 for (size_t i = 0; i < clip_path.GetPathCount(); ++i) {
615 ProcessPathPoints(buf, &path);
616 switch (clip_path.GetClipType(i)) {
644 m_pObjHolder->GraphicsMapSearch(graphD);
645 if (maybe_name.has_value()) {
646 name =
std::move(maybe_name.value());
650 gsDict->SetNewFor<CPDF_Number>(
"ca", graphD
.fillAlpha);
653 gsDict->SetNewFor<CPDF_Number>(
"CA", graphD
.strokeAlpha);
656 gsDict->SetNewFor<CPDF_Name>(
"BM",
659 m_pDocument->AddIndirectObject(gsDict);
660 name = RealizeResource(
std::move(gsDict),
"ExtGState");
662 m_pObjHolder->GraphicsMapInsert(graphD, name);
668 fxcrt::ostringstream* buf) {
669 *buf <<
"0 0 0 RG 0 0 0 rg 1 w "
672 m_DefaultGraphicsName = GetOrCreateDefaultGraphics();
673 *buf <<
"/" << PDF_NameEncode(m_DefaultGraphicsName) <<
" gs ";
683 m_pObjHolder->GraphicsMapSearch(defaultGraphics);
684 if (maybe_name.has_value())
685 return maybe_name.value();
688 gsDict->SetNewFor<CPDF_Number>(
"ca", defaultGraphics
.fillAlpha);
689 gsDict->SetNewFor<CPDF_Number>(
"CA", defaultGraphics
.strokeAlpha);
690 gsDict->SetNewFor<CPDF_Name>(
"BM",
"Normal");
691 m_pDocument->AddIndirectObject(gsDict);
692 ByteString name = RealizeResource(
std::move(gsDict),
"ExtGState");
693 m_pObjHolder->GraphicsMapInsert(defaultGraphics, name);
703 CPDF_TextObject* pTextObj) {
704 ProcessGraphics(buf, pTextObj);
709 WriteMatrix(*buf, matrix) <<
" Tm ";
714 pFont = CPDF_Font::GetStockFont(m_pDocument,
"Helvetica");
718 if (pFont->IsType1Font()) {
720 pEncoding = pFont->AsType1Font()->GetEncoding();
721 }
else if (pFont->IsTrueTypeFont()) {
722 data.type =
"TrueType";
723 pEncoding = pFont->AsTrueTypeFont()->GetEncoding();
724 }
else if (pFont->IsCIDFont()) {
729 data.baseFont = pFont->GetBaseFontName();
732 std::optional<
ByteString> maybe_name = m_pObjHolder->FontsMapSearch(data);
733 if (maybe_name.has_value()) {
734 dict_name =
std::move(maybe_name.value());
737 if (pIndirectFont->IsInline()) {
740 pFontDict->SetNewFor<CPDF_Name>(
"Type",
"Font");
741 pFontDict->SetNewFor<CPDF_Name>(
"Subtype", data.type);
742 pFontDict->SetNewFor<CPDF_Name>(
"BaseFont", data.baseFont);
744 pFontDict->SetFor(
"Encoding",
745 pEncoding->Realize(m_pDocument->GetByteStringPool()));
747 m_pDocument->AddIndirectObject(pFontDict);
748 pIndirectFont =
std::move(pFontDict);
750 dict_name = RealizeResource(
std::move(pIndirectFont),
"Font");
751 m_pObjHolder->FontsMapInsert(data, dict_name);
759 for (uint32_t charcode : pTextObj->GetCharCodes()) {
760 if (charcode != CPDF_Font::kInvalidCharCode) {
761 pFont->AppendChar(&text, charcode);
764 *buf << PDF_HexEncodeString(text.AsStringView()) <<
" Tj ET";
fxcrt::ByteString ByteString
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 IsColorSpaceGray() const
bool IsColorSpaceRGB() const
const ByteString & GetName() const
const ByteString & GetPropertyName() const
ParamType GetParamType() const
std::map< ByteString, RetainPtr< CPDF_Object >, std::less<> > DictMap
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
ByteString PDF_NameEncode(const ByteString &orig)
CFX_PTemplate< float > CFX_PointF
#define NOTREACHED_NORETURN()