summaryrefslogtreecommitdiff
path: root/core/fpdfapi/edit
diff options
context:
space:
mode:
Diffstat (limited to 'core/fpdfapi/edit')
-rw-r--r--core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp71
-rw-r--r--core/fpdfapi/edit/cpdf_pagecontentgenerator.h12
-rw-r--r--core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp82
3 files changed, 153 insertions, 12 deletions
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
index e2354e1b50..9dba6e0ceb 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp
@@ -6,6 +6,9 @@
#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
+#include <tuple>
+#include <utility>
+
#include "core/fpdfapi/page/cpdf_docpagedata.h"
#include "core/fpdfapi/page/cpdf_image.h"
#include "core/fpdfapi/page/cpdf_imageobject.h"
@@ -15,6 +18,7 @@
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_reference.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/fpdf_parser_decode.h"
@@ -27,6 +31,19 @@ CFX_ByteTextBuf& operator<<(CFX_ByteTextBuf& ar, const CFX_Matrix& matrix) {
return ar;
}
+bool GetColor(const CPDF_Color* pColor, FX_FLOAT* rgb) {
+ int intRGB[3];
+ if (!pColor ||
+ pColor->GetColorSpace() != CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB) ||
+ !pColor->GetRGB(intRGB[0], intRGB[1], intRGB[2])) {
+ return false;
+ }
+ rgb[0] = intRGB[0] / 255.0f;
+ rgb[1] = intRGB[1] / 255.0f;
+ rgb[2] = intRGB[2] / 255.0f;
+ return true;
+}
+
} // namespace
CPDF_PageContentGenerator::CPDF_PageContentGenerator(CPDF_Page* pPage)
@@ -123,8 +140,10 @@ void CPDF_PageContentGenerator::ProcessImage(CFX_ByteTextBuf* buf,
// "h" closes the subpath (appends a line from current to starting point)
// Path painting operators: "S", "n", "B", "f", "B*", "f*", depending on
// the filling mode and whether we want stroking the path or not.
+// "Q" restores the graphics state imposed by the ProcessGraphics method.
void CPDF_PageContentGenerator::ProcessPath(CFX_ByteTextBuf* buf,
CPDF_PathObject* pPathObj) {
+ ProcessGraphics(buf, pPathObj);
const FX_PATHPOINT* pPoints = pPathObj->m_Path.GetPoints();
if (pPathObj->m_Path.IsRect()) {
*buf << pPoints[0].m_PointX << " " << pPoints[0].m_PointY << " "
@@ -166,5 +185,55 @@ void CPDF_PageContentGenerator::ProcessPath(CFX_ByteTextBuf* buf,
*buf << (pPathObj->m_bStroke ? " B" : " f");
else if (pPathObj->m_FillType == FXFILL_ALTERNATE)
*buf << (pPathObj->m_bStroke ? " B*" : " f*");
- *buf << "\n";
+ *buf << " Q\n";
+}
+
+// This method supports color operators rg and RGB from Table 4.24 of PDF spec
+// 1.7. A color will not be set if the colorspace is not DefaultRGB or the RGB
+// values cannot be obtained. The method also adds an external graphics
+// dictionary, as described in Section 4.3.4.
+// "rg" sets the fill color, "RG" sets the stroke color (using DefaultRGB)
+// "ca" sets the fill alpha, "CA" sets the stroke alpha.
+// "q" saves the graphics state, so that the settings can later be reversed
+void CPDF_PageContentGenerator::ProcessGraphics(CFX_ByteTextBuf* buf,
+ CPDF_PageObject* pPageObj) {
+ *buf << "q ";
+ FX_FLOAT fillColor[3];
+ if (GetColor(pPageObj->m_ColorState.GetFillColor(), fillColor)) {
+ *buf << fillColor[0] << " " << fillColor[1] << " " << fillColor[2]
+ << " rg ";
+ }
+ FX_FLOAT strokeColor[3];
+ if (GetColor(pPageObj->m_ColorState.GetStrokeColor(), strokeColor)) {
+ *buf << strokeColor[0] << " " << strokeColor[1] << " " << strokeColor[2]
+ << " RG ";
+ }
+
+ GraphicsData graphD;
+ graphD.fillAlpha = pPageObj->m_GeneralState.GetFillAlpha();
+ graphD.strokeAlpha = pPageObj->m_GeneralState.GetStrokeAlpha();
+ if (graphD.fillAlpha == 1.0f && graphD.strokeAlpha == 1.0f)
+ return;
+
+ CFX_ByteString name;
+ auto it = m_GraphicsMap.find(graphD);
+ if (it != m_GraphicsMap.end()) {
+ name = it->second;
+ } else {
+ auto gsDict = pdfium::MakeUnique<CPDF_Dictionary>();
+ gsDict->SetNewFor<CPDF_Number>("ca", graphD.fillAlpha);
+ gsDict->SetNewFor<CPDF_Number>("CA", graphD.strokeAlpha);
+ CPDF_Object* pDict = m_pDocument->AddIndirectObject(std::move(gsDict));
+ uint32_t dwObjNum = pDict->GetObjNum();
+ name = RealizeResource(dwObjNum, "ExtGState");
+ m_GraphicsMap[graphD] = name;
+ }
+ *buf << "/" << PDF_NameEncode(name) << " gs ";
+}
+
+bool CPDF_PageContentGenerator::GraphicsData::operator<(
+ const GraphicsData& other) const {
+ if (fillAlpha != other.fillAlpha)
+ return fillAlpha < other.fillAlpha;
+ return strokeAlpha < other.strokeAlpha;
}
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
index c74652e206..e48ea4a7c9 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h
@@ -7,6 +7,7 @@
#ifndef CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_
#define CORE_FPDFAPI_EDIT_CPDF_PAGECONTENTGENERATOR_H_
+#include <map>
#include <vector>
#include "core/fxcrt/fx_basic.h"
@@ -26,14 +27,21 @@ class CPDF_PageContentGenerator {
void GenerateContent();
private:
- friend class cpdf_pagecontentgenerator_ProcessRect_Test;
- friend class cpdf_pagecontentgenerator_ProcessPath_Test;
+ friend class CPDF_PageContentGeneratorTest;
void ProcessPath(CFX_ByteTextBuf* buf, CPDF_PathObject* pPathObj);
void ProcessImage(CFX_ByteTextBuf* buf, CPDF_ImageObject* pImageObj);
+ void ProcessGraphics(CFX_ByteTextBuf* buf, CPDF_PageObject* pPageObj);
CFX_ByteString RealizeResource(uint32_t dwResourceObjNum,
const CFX_ByteString& bsType);
+ struct GraphicsData {
+ FX_FLOAT fillAlpha;
+ FX_FLOAT strokeAlpha;
+ bool operator<(const GraphicsData& other) const;
+ };
+
+ std::map<GraphicsData, CFX_ByteString> m_GraphicsMap;
CPDF_Page* const m_pPage;
CPDF_Document* const m_pDocument;
std::vector<CPDF_PageObject*> m_pageObjects;
diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
index 8812c0e2b6..41e61b3bab 100644
--- a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
+++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp
@@ -4,12 +4,34 @@
#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h"
+#include "core/fpdfapi/cpdf_modulemgr.h"
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/page/cpdf_pathobject.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/base/ptr_util.h"
-TEST(cpdf_pagecontentgenerator, ProcessRect) {
+class CPDF_PageContentGeneratorTest : public testing::Test {
+ protected:
+ void SetUp() override { CPDF_ModuleMgr::Get()->InitPageModule(); }
+
+ void TearDown() override { CPDF_ModuleMgr::Destroy(); }
+
+ void TestProcessPath(CPDF_PageContentGenerator* pGen,
+ CFX_ByteTextBuf* buf,
+ CPDF_PathObject* pPathObj) {
+ pGen->ProcessPath(buf, pPathObj);
+ }
+
+ CPDF_Dictionary* TestGetGS(CPDF_PageContentGenerator* pGen,
+ const CFX_ByteString& name) {
+ return pGen->m_pPage->m_pResources->GetDictFor("ExtGState")
+ ->GetDictFor(name);
+ }
+};
+
+TEST_F(CPDF_PageContentGeneratorTest, ProcessRect) {
auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
pPathObj->m_Path.AppendRect(10, 5, 13, 30);
pPathObj->m_FillType = FXFILL_ALTERNATE;
@@ -17,8 +39,8 @@ TEST(cpdf_pagecontentgenerator, ProcessRect) {
auto pTestPage = pdfium::MakeUnique<CPDF_Page>(nullptr, nullptr, false);
CPDF_PageContentGenerator generator(pTestPage.get());
CFX_ByteTextBuf buf;
- generator.ProcessPath(&buf, pPathObj.get());
- EXPECT_EQ("10 5 3 25 re B*\n", buf.MakeString());
+ TestProcessPath(&generator, &buf, pPathObj.get());
+ EXPECT_EQ("q 10 5 3 25 re B* Q\n", buf.MakeString());
pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
pPathObj->m_Path.SetPointCount(4);
@@ -38,11 +60,11 @@ TEST(cpdf_pagecontentgenerator, ProcessRect) {
pPathObj->m_FillType = 0;
pPathObj->m_bStroke = false;
buf.Clear();
- generator.ProcessPath(&buf, pPathObj.get());
- EXPECT_EQ("0 0 5.2 3.78 re n\n", buf.MakeString());
+ TestProcessPath(&generator, &buf, pPathObj.get());
+ EXPECT_EQ("q 0 0 5.2 3.78 re n Q\n", buf.MakeString());
}
-TEST(cpdf_pagecontentgenerator, ProcessPath) {
+TEST_F(CPDF_PageContentGeneratorTest, ProcessPath) {
auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
pPathObj->m_Path.SetPointCount(10);
FX_PATHPOINT* pPoints = pPathObj->m_Path.GetMutablePoints();
@@ -81,9 +103,51 @@ TEST(cpdf_pagecontentgenerator, ProcessPath) {
auto pTestPage = pdfium::MakeUnique<CPDF_Page>(nullptr, nullptr, false);
CPDF_PageContentGenerator generator(pTestPage.get());
CFX_ByteTextBuf buf;
- generator.ProcessPath(&buf, pPathObj.get());
+ TestProcessPath(&generator, &buf, pPathObj.get());
EXPECT_EQ(
- "3.102 4.67 m 5.45 0.29 l 4.24 3.15 4.65 2.98 3.456 0.24 c 10.6 11.15 l "
- "11 12.5 l 11.46 12.67 11.84 12.96 12 13.64 c h f\n",
+ "q 3.102 4.67 m 5.45 0.29 l 4.24 3.15 4.65 2.98 3.456 0.24 c 10.6 11.15 "
+ "l 11 12.5 l 11.46 12.67 11.84 12.96 12 13.64 c h f Q\n",
buf.MakeString());
}
+
+TEST_F(CPDF_PageContentGeneratorTest, ProcessGraphics) {
+ auto pPathObj = pdfium::MakeUnique<CPDF_PathObject>();
+ pPathObj->m_Path.SetPointCount(3);
+ FX_PATHPOINT* pPoints = pPathObj->m_Path.GetMutablePoints();
+ pPoints[0].m_PointX = 1;
+ pPoints[0].m_PointY = 2;
+ pPoints[0].m_Flag = FXPT_MOVETO;
+ pPoints[1].m_PointX = 3;
+ pPoints[1].m_PointY = 4;
+ pPoints[1].m_Flag = FXPT_LINETO;
+ pPoints[2].m_PointX = 5;
+ pPoints[2].m_PointY = 6;
+ pPoints[2].m_Flag = FXPT_LINETO | FXPT_CLOSEFIGURE;
+ pPathObj->m_FillType = FXFILL_WINDING;
+ pPathObj->m_bStroke = true;
+ FX_FLOAT rgb[3] = {0.5f, 0.7f, 0.35f};
+ CPDF_ColorSpace* pCS = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB);
+ pPathObj->m_ColorState.SetFillColor(pCS, rgb, 3);
+ FX_FLOAT rgb2[3] = {1, 0.9f, 0};
+ pPathObj->m_ColorState.SetStrokeColor(pCS, rgb2, 3);
+ pPathObj->m_GeneralState.SetFillAlpha(0.5f);
+ pPathObj->m_GeneralState.SetStrokeAlpha(0.8f);
+ auto pDoc = pdfium::MakeUnique<CPDF_Document>(nullptr);
+ pDoc->CreateNewDoc();
+ CPDF_Dictionary* pPageDict = pDoc->CreateNewPage(0);
+ auto pTestPage = pdfium::MakeUnique<CPDF_Page>(pDoc.get(), pPageDict, false);
+ CPDF_PageContentGenerator generator(pTestPage.get());
+ CFX_ByteTextBuf buf;
+ TestProcessPath(&generator, &buf, pPathObj.get());
+ CFX_ByteString pathString = buf.MakeString();
+ // Color RGB values used are integers divided by 255.
+ EXPECT_EQ("q 0.501961 0.701961 0.34902 rg 1 0.901961 0 RG /",
+ pathString.Left(48));
+ EXPECT_EQ(" gs 1 2 m 3 4 l 5 6 l h B Q\n", pathString.Right(28));
+ ASSERT_TRUE(pathString.GetLength() > 76);
+ CPDF_Dictionary* externalGS =
+ TestGetGS(&generator, pathString.Mid(48, pathString.GetLength() - 76));
+ ASSERT_TRUE(externalGS);
+ EXPECT_EQ(0.5f, externalGS->GetNumberFor("ca"));
+ EXPECT_EQ(0.8f, externalGS->GetNumberFor("CA"));
+}