diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp | 71 | ||||
-rw-r--r-- | core/fpdfapi/edit/cpdf_pagecontentgenerator.h | 12 | ||||
-rw-r--r-- | core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp | 82 |
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")); +} |