summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Pena <npm@chromium.org>2017-06-28 15:31:56 -0400
committerChromium commit bot <commit-bot@chromium.org>2017-06-28 19:58:25 +0000
commit9ba8fbc02e86a6b7cb5c4142d9481ae3238c1ec4 (patch)
tree3121a73de34a92edd84b982e04b2b900a878e90c
parentd4fb57a4e23ccc2e374c64ea5d5705f492fdd083 (diff)
downloadpdfium-9ba8fbc02e86a6b7cb5c4142d9481ae3238c1ec4.tar.xz
Set default graphics before generating page contents
In this CL, the content generator sets some default graphics states before processing the page objects. In particular, a default ExtGState is now set before processing, and the last CTM is now stored right after parsing finishes: the only command to change matrix is ctm, and it concatenates, so inverting requires knowing the current value. Bug: pdfium:779 Change-Id: I35b1c07550ce91839fb0e20fbf717e3e80c9b9d6 Reviewed-on: https://pdfium-review.googlesource.com/7070 Commit-Queue: Nicolás Peña <npm@chromium.org> Reviewed-by: dsinclair <dsinclair@chromium.org>
-rw-r--r--core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp42
-rw-r--r--core/fpdfapi/edit/cpdf_pagecontentgenerator.h1
-rw-r--r--core/fpdfapi/page/cpdf_contentparser.h1
-rw-r--r--core/fpdfapi/page/cpdf_pageobjectholder.cpp3
-rw-r--r--core/fpdfapi/page/cpdf_pageobjectholder.h2
-rw-r--r--fpdfsdk/fpdfedit_embeddertest.cpp79
-rw-r--r--testing/resources/bug_779.in50
-rw-r--r--testing/resources/bug_779.pdf60
8 files changed, 225 insertions, 13 deletions
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index eeb6f81721..10b3933742 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -65,9 +65,24 @@ void CPDF_PageContentGenerator::GenerateContent() {
CPDF_Document* pDoc = m_pDocument.Get();
std::ostringstream buf;
+
+ // Set the default graphic state values
+ buf << "q\n";
+ if (!m_pObjHolder->GetLastCTM().IsIdentity()) {
+ CFX_Matrix reverse;
+ reverse.SetReverse(m_pObjHolder->GetLastCTM());
+ buf << reverse << " cm\n";
+ }
+ ProcessDefaultGraphics(&buf);
+
+ // Process the page objects
if (!ProcessPageObjects(&buf))
return;
+ // Return graphics to original state
+ buf << "Q\n";
+
+ // Add buffer to a stream in page's 'Contents'
CPDF_Dictionary* pPageDict = m_pObjHolder->m_pFormDict.Get();
CPDF_Object* pContent =
pPageDict ? pPageDict->GetObjectFor("Contents") : nullptr;
@@ -142,6 +157,7 @@ bool CPDF_PageContentGenerator::ProcessPageObjects(std::ostringstream* buf) {
for (auto& pPageObj : m_pageObjects) {
if (m_pObjHolder->IsPage() && !pPageObj->IsDirty())
continue;
+
bDirty = true;
if (CPDF_ImageObject* pImageObject = pPageObj->AsImage())
ProcessImage(buf, pImageObject);
@@ -304,6 +320,32 @@ void CPDF_PageContentGenerator::ProcessGraphics(std::ostringstream* buf,
*buf << "/" << PDF_NameEncode(name) << " gs ";
}
+void CPDF_PageContentGenerator::ProcessDefaultGraphics(
+ std::ostringstream* buf) {
+ *buf << "0 0 0 RG 0 0 0 rg 1 w "
+ << static_cast<int>(CFX_GraphStateData::LineCapButt) << " J "
+ << static_cast<int>(CFX_GraphStateData::LineJoinMiter) << " j\n";
+ GraphicsData defaultGraphics;
+ defaultGraphics.fillAlpha = 1.0f;
+ defaultGraphics.strokeAlpha = 1.0f;
+ defaultGraphics.blendType = FXDIB_BLEND_NORMAL;
+ auto it = m_pObjHolder->m_GraphicsMap.find(defaultGraphics);
+ CFX_ByteString name;
+ if (it != m_pObjHolder->m_GraphicsMap.end()) {
+ name = it->second;
+ } else {
+ auto gsDict = pdfium::MakeUnique<CPDF_Dictionary>();
+ gsDict->SetNewFor<CPDF_Number>("ca", defaultGraphics.fillAlpha);
+ gsDict->SetNewFor<CPDF_Number>("CA", defaultGraphics.strokeAlpha);
+ gsDict->SetNewFor<CPDF_Name>("BM", "Normal");
+ CPDF_Object* pDict = m_pDocument->AddIndirectObject(std::move(gsDict));
+ uint32_t dwObjNum = pDict->GetObjNum();
+ name = RealizeResource(dwObjNum, "ExtGState");
+ m_pObjHolder->m_GraphicsMap[defaultGraphics] = name;
+ }
+ *buf << "/" << PDF_NameEncode(name).c_str() << " gs ";
+}
+
// This method adds text to the buffer, BT begins the text object, ET ends it.
// Tm sets the text matrix (allows positioning and transforming text).
// Tf sets the font name (from Font in Resources) and font size.
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
index 2d90eb4465..5295d8747b 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
@@ -35,6 +35,7 @@ class CPDF_PageContentGenerator {
void ProcessPath(std::ostringstream* buf, CPDF_PathObject* pPathObj);
void ProcessImage(std::ostringstream* buf, CPDF_ImageObject* pImageObj);
void ProcessGraphics(std::ostringstream* buf, CPDF_PageObject* pPageObj);
+ void ProcessDefaultGraphics(std::ostringstream* buf);
void ProcessText(std::ostringstream* buf, CPDF_TextObject* pTextObj);
CFX_ByteString RealizeResource(uint32_t dwResourceObjNum,
const CFX_ByteString& bsType);
diff --git a/core/fpdfapi/page/cpdf_contentparser.h b/core/fpdfapi/page/cpdf_contentparser.h
index 58a977301f..b18e07036c 100644
--- a/core/fpdfapi/page/cpdf_contentparser.h
+++ b/core/fpdfapi/page/cpdf_contentparser.h
@@ -29,6 +29,7 @@ class CPDF_ContentParser {
~CPDF_ContentParser();
ParseStatus GetStatus() const { return m_Status; }
+ CPDF_StreamContentParser* GetParser() const { return m_pParser.get(); }
void Start(CPDF_Page* pPage);
void Start(CPDF_Form* pForm,
CPDF_AllStates* pGraphicStates,
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.cpp b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
index 50ef780396..614fa34d2b 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.cpp
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
@@ -8,6 +8,7 @@
#include <algorithm>
+#include "core/fpdfapi/page/cpdf_allstates.h"
#include "core/fpdfapi/page/cpdf_contentparser.h"
#include "core/fpdfapi/page/cpdf_pageobject.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -38,6 +39,8 @@ void CPDF_PageObjectHolder::ContinueParse(IFX_Pause* pPause) {
return;
m_ParseState = CONTENT_PARSED;
+ if (m_pParser->GetParser() && m_pParser->GetParser()->GetCurStates())
+ m_LastCTM = m_pParser->GetParser()->GetCurStates()->m_CTM;
m_pParser.reset();
}
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.h b/core/fpdfapi/page/cpdf_pageobjectholder.h
index 4733e06b2c..87ebbc8460 100644
--- a/core/fpdfapi/page/cpdf_pageobjectholder.h
+++ b/core/fpdfapi/page/cpdf_pageobjectholder.h
@@ -55,6 +55,7 @@ class CPDF_PageObjectHolder {
const CPDF_PageObjectList* GetPageObjectList() const {
return &m_PageObjectList;
}
+ const CFX_Matrix& GetLastCTM() const { return m_LastCTM; }
bool BackgroundAlphaNeeded() const { return m_bBackgroundAlphaNeeded; }
void SetBackgroundAlphaNeeded(bool needed) {
@@ -89,6 +90,7 @@ class CPDF_PageObjectHolder {
ParseState m_ParseState;
std::unique_ptr<CPDF_ContentParser> m_pParser;
CPDF_PageObjectList m_PageObjectList;
+ CFX_Matrix m_LastCTM;
};
#endif // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_
diff --git a/fpdfsdk/fpdfedit_embeddertest.cpp b/fpdfsdk/fpdfedit_embeddertest.cpp
index ad3e0d15a8..727306a96e 100644
--- a/fpdfsdk/fpdfedit_embeddertest.cpp
+++ b/fpdfsdk/fpdfedit_embeddertest.cpp
@@ -139,24 +139,29 @@ const char kExpectedPDF[] =
"<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
"endobj\r\n"
"4 0 obj\r\n"
- "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R /Resources<<>>"
+ "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R "
+ "/Resources<</ExtGState<</FXE1 5 0 R >>>>"
"/Rotate 0/Type/Page"
">>\r\n"
"endobj\r\n"
+ "5 0 obj\r\n"
+ "<</BM/Normal/CA 1/ca 1>>\r\n"
+ "endobj\r\n"
"xref\r\n"
- "0 5\r\n"
+ "0 6\r\n"
"0000000000 65535 f\r\n"
"0000000017 00000 n\r\n"
"0000000066 00000 n\r\n"
"0000000122 00000 n\r\n"
"0000000192 00000 n\r\n"
+ "0000000311 00000 n\r\n"
"trailer\r\n"
"<<\r\n"
"/Root 1 0 R\r\n"
"/Info 3 0 R\r\n"
- "/Size 5/ID\\[<.*><.*>\\]>>\r\n"
+ "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
"startxref\r\n"
- "285\r\n"
+ "354\r\n"
"%%EOF\r\n";
} // namespace
@@ -572,7 +577,7 @@ TEST_F(FPDFEditEmbeddertest, GraphicsData) {
CPDF_Dictionary* graphics_dict =
the_page->m_pResources->GetDictFor("ExtGState");
ASSERT_TRUE(graphics_dict);
- EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
// Add a text object causing no change to the graphics dictionary
FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
@@ -581,7 +586,7 @@ TEST_F(FPDFEditEmbeddertest, GraphicsData) {
EXPECT_TRUE(FPDFText_SetFillColor(text1, 100, 100, 100, 255));
FPDFPage_InsertObject(page.get(), text1);
EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
- EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
// Add a text object increasing the size of the graphics dictionary
FPDF_PAGEOBJECT text2 =
@@ -590,7 +595,7 @@ TEST_F(FPDFEditEmbeddertest, GraphicsData) {
FPDFPageObj_SetBlendMode(text2, "Darken");
EXPECT_TRUE(FPDFText_SetFillColor(text2, 0, 0, 255, 150));
EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
- EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
// Add a path that should reuse graphics
FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
@@ -598,7 +603,7 @@ TEST_F(FPDFEditEmbeddertest, GraphicsData) {
EXPECT_TRUE(FPDFPath_SetFillColor(path, 200, 200, 100, 150));
FPDFPage_InsertObject(page.get(), path);
EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
- EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
// Add a rect increasing the size of the graphics dictionary
FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
@@ -607,7 +612,7 @@ TEST_F(FPDFEditEmbeddertest, GraphicsData) {
EXPECT_TRUE(FPDFPath_SetStrokeColor(rect2, 0, 0, 0, 200));
FPDFPage_InsertObject(page.get(), rect2);
EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
- EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(4, static_cast<int>(graphics_dict->GetCount()));
}
TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
@@ -626,7 +631,7 @@ TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
CPDF_Dictionary* graphics_dict =
the_page->m_pResources->GetDictFor("ExtGState");
ASSERT_TRUE(graphics_dict);
- EXPECT_EQ(1, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
// Check the bitmap
FPDF_BITMAP page_bitmap = RenderPage(page);
@@ -636,7 +641,7 @@ TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
// Never mind, my new favorite color is blue, increase alpha
EXPECT_TRUE(FPDFPath_SetFillColor(rect, 0, 0, 255, 180));
EXPECT_TRUE(FPDFPage_GenerateContent(page));
- EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
// Check that bitmap displays changed content
page_bitmap = RenderPage(page);
@@ -645,7 +650,7 @@ TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
// And now generate, without changes
EXPECT_TRUE(FPDFPage_GenerateContent(page));
- EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
page_bitmap = RenderPage(page);
CompareBitmap(page_bitmap, 612, 792, "2e51656f5073b0bee611d9cd086aa09c");
FPDFBitmap_Destroy(page_bitmap);
@@ -665,7 +670,7 @@ TEST_F(FPDFEditEmbeddertest, DoubleGenerating) {
// Generate yet again, check dicts are reasonably sized
EXPECT_TRUE(FPDFPage_GenerateContent(page));
- EXPECT_EQ(2, static_cast<int>(graphics_dict->GetCount()));
+ EXPECT_EQ(3, static_cast<int>(graphics_dict->GetCount()));
EXPECT_EQ(1, static_cast<int>(font_dict->GetCount()));
FPDF_ClosePage(page);
}
@@ -984,3 +989,51 @@ TEST_F(FPDFEditEmbeddertest, AddCIDFontText) {
FPDF_CloseDocument(new_doc);
}
#endif // _FXM_PLATFORM_ == _FXM_PLATFORM_LINUX_
+
+TEST_F(FPDFEditEmbeddertest, SaveAndRender) {
+ const char embMD5[] = "3c20472b0552c0c22b88ab1ed8c6202b";
+ {
+ EXPECT_TRUE(OpenDocument("bug_779.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_NE(nullptr, page);
+
+ // Now add a more complex blue path.
+ FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20);
+ EXPECT_TRUE(FPDFPath_SetFillColor(green_path, 0, 255, 0, 200));
+ // TODO(npm): stroking will cause the MD5s to differ.
+ EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0));
+ EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63));
+ EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90));
+ EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 133));
+ EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 33));
+ EXPECT_TRUE(FPDFPath_BezierTo(green_path, 38, 33, 39, 36, 40, 40));
+ EXPECT_TRUE(FPDFPath_Close(green_path));
+ FPDFPage_InsertObject(page, green_path);
+ FPDF_BITMAP page_bitmap = RenderPage(page);
+ CompareBitmap(page_bitmap, 612, 792, embMD5);
+ FPDFBitmap_Destroy(page_bitmap);
+
+ // Now save the result, closing the page and document
+ EXPECT_TRUE(FPDFPage_GenerateContent(page));
+ EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+ UnloadPage(page);
+ }
+
+ // Render the saved result
+ std::string new_file = GetString();
+ FPDF_FILEACCESS file_access;
+ memset(&file_access, 0, sizeof(file_access));
+ file_access.m_FileLen = new_file.size();
+ file_access.m_GetBlock = GetBlockFromString;
+ file_access.m_Param = &new_file;
+ FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
+ ASSERT_NE(nullptr, new_doc);
+ EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
+ FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
+ ASSERT_NE(nullptr, new_page);
+ FPDF_BITMAP new_bitmap = RenderPage(new_page);
+ CompareBitmap(new_bitmap, 612, 792, embMD5);
+ FPDFBitmap_Destroy(new_bitmap);
+ FPDF_ClosePage(new_page);
+ FPDF_CloseDocument(new_doc);
+}
diff --git a/testing/resources/bug_779.in b/testing/resources/bug_779.in
new file mode 100644
index 0000000000..f8e21a0aad
--- /dev/null
+++ b/testing/resources/bug_779.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [ 0 0 612 792 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+>>
+stream
+1 0 0 -1 0 792 cm
+0 0 255 RG
+255 0 0 rg
+5 w
+/GS1 gs
+q
+100 100 m
+200 200 l
+200 300 l
+b*
+endstream
+endobj
+{{object 5 0}} <<
+ /ExtGState <<
+ /GS1 6 0 R
+ >>
+>>
+endobj
+{{object 6 0}} <<
+ /Type /ExtGState
+ /CA 0.3
+ /ca 0.3
+>>
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF \ No newline at end of file
diff --git a/testing/resources/bug_779.pdf b/testing/resources/bug_779.pdf
new file mode 100644
index 0000000000..2d609045cc
--- /dev/null
+++ b/testing/resources/bug_779.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [ 0 0 612 792 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources 5 0 R
+>>
+endobj
+4 0 obj <<
+>>
+stream
+1 0 0 -1 0 792 cm
+0 0 255 RG
+255 0 0 rg
+5 w
+/GS1 gs
+q
+100 100 m
+200 200 l
+200 300 l
+b*
+endstream
+endobj
+5 0 obj <<
+ /ExtGState <<
+ /GS1 6 0 R
+ >>
+>>
+endobj
+6 0 obj <<
+ /Type /ExtGState
+ /CA 0.3
+ /ca 0.3
+>>
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000161 00000 n
+0000000249 00000 n
+0000000374 00000 n
+0000000431 00000 n
+trailer<< /Root 1 0 R /Size 7 >>
+startxref
+484
+%%EOF \ No newline at end of file