From 4bd0d99c11102514fb43328b67b7674165888619 Mon Sep 17 00:00:00 2001 From: Nicolas Pena Date: Thu, 2 Feb 2017 14:32:21 -0500 Subject: Add CPDF_PageContentGenerator::ProcessPath supporting path construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See Table 4.9 for path construction operators. Ignoring v, y, because those can be replaced with the more powerful c operator. The code added will be called by public methods that do not exist yet. BUG=pdfium:661 Change-Id: I9a1ad3fd0a601e4e3a292b55f3e5708fe0c9c8fb Reviewed-on: https://pdfium-review.googlesource.com/2495 Commit-Queue: Nicolás Peña Reviewed-by: dsinclair --- BUILD.gn | 1 + core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp | 63 ++++++++++++++- core/fpdfapi/edit/cpdf_pagecontentgenerator.h | 5 ++ .../edit/cpdf_pagecontentgenerator_unittest.cpp | 89 ++++++++++++++++++++++ 4 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp diff --git a/BUILD.gn b/BUILD.gn index c7266d3a1d..d0946a490d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1734,6 +1734,7 @@ if (pdf_enable_xfa) { test("pdfium_unittests") { sources = [ "core/fdrm/crypto/fx_crypt_unittest.cpp", + "core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp", "core/fpdfapi/font/fpdf_font_cid_unittest.cpp", "core/fpdfapi/font/fpdf_font_unittest.cpp", "core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp", diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp index a65d564141..e2354e1b50 100644 --- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp +++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.cpp @@ -10,6 +10,8 @@ #include "core/fpdfapi/page/cpdf_image.h" #include "core/fpdfapi/page/cpdf_imageobject.h" #include "core/fpdfapi/page/cpdf_page.h" +#include "core/fpdfapi/page/cpdf_path.h" +#include "core/fpdfapi/page/cpdf_pathobject.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_document.h" #include "core/fpdfapi/parser/cpdf_name.h" @@ -40,9 +42,10 @@ CPDF_PageContentGenerator::~CPDF_PageContentGenerator() {} void CPDF_PageContentGenerator::GenerateContent() { CFX_ByteTextBuf buf; for (CPDF_PageObject* pPageObj : m_pageObjects) { - CPDF_ImageObject* pImageObject = pPageObj->AsImage(); - if (pImageObject) + if (CPDF_ImageObject* pImageObject = pPageObj->AsImage()) ProcessImage(&buf, pImageObject); + else if (CPDF_PathObject* pPathObj = pPageObj->AsPath()) + ProcessPath(&buf, pPathObj); } CPDF_Dictionary* pPageDict = m_pPage->m_pFormDict; CPDF_Object* pContent = @@ -109,3 +112,59 @@ void CPDF_PageContentGenerator::ProcessImage(CFX_ByteTextBuf* buf, *buf << "/" << PDF_NameEncode(name) << " Do Q\n"; } + +// Processing path with operators from Tables 4.9 and 4.10 of PDF spec 1.7: +// "re" appends a rectangle (here, used only if the whole path is a rectangle) +// "m" moves current point to the given coordinates +// "l" creates a line from current point to the new point +// "c" adds a Bezier curve from current to last point, using the two other +// points as the Bezier control points +// Note: "l", "c" change the current point +// "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. +void CPDF_PageContentGenerator::ProcessPath(CFX_ByteTextBuf* buf, + CPDF_PathObject* pPathObj) { + const FX_PATHPOINT* pPoints = pPathObj->m_Path.GetPoints(); + if (pPathObj->m_Path.IsRect()) { + *buf << pPoints[0].m_PointX << " " << pPoints[0].m_PointY << " " + << (pPoints[2].m_PointX - pPoints[0].m_PointX) << " " + << (pPoints[2].m_PointY - pPoints[0].m_PointY) << " re"; + } else { + int numPoints = pPathObj->m_Path.GetPointCount(); + for (int i = 0; i < numPoints; i++) { + if (i > 0) + *buf << " "; + *buf << pPoints[i].m_PointX << " " << pPoints[i].m_PointY; + int pointFlag = pPoints[i].m_Flag; + if (pointFlag == FXPT_MOVETO) { + *buf << " m"; + } else if (pointFlag & FXPT_LINETO) { + *buf << " l"; + } else if (pointFlag & FXPT_BEZIERTO) { + if (i + 2 >= numPoints || pPoints[i].m_Flag != FXPT_BEZIERTO || + pPoints[i + 1].m_Flag != FXPT_BEZIERTO || + (pPoints[i + 2].m_Flag & FXPT_BEZIERTO) == 0) { + // If format is not supported, close the path and paint + *buf << " h"; + break; + } + *buf << " " << pPoints[i + 1].m_PointX << " " << pPoints[i + 1].m_PointY + << " " << pPoints[i + 2].m_PointX << " " << pPoints[i + 2].m_PointY + << " c"; + if (pPoints[i + 2].m_Flag & FXPT_CLOSEFIGURE) + *buf << " h"; + i += 2; + } + if (pointFlag & FXPT_CLOSEFIGURE) + *buf << " h"; + } + } + if (pPathObj->m_FillType == 0) + *buf << (pPathObj->m_bStroke ? " S" : " n"); + else if (pPathObj->m_FillType == FXFILL_WINDING) + *buf << (pPathObj->m_bStroke ? " B" : " f"); + else if (pPathObj->m_FillType == FXFILL_ALTERNATE) + *buf << (pPathObj->m_bStroke ? " B*" : " f*"); + *buf << "\n"; +} diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h index ac06dcb554..c74652e206 100644 --- a/core/fpdfapi/edit/cpdf_pagecontentgenerator.h +++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator.h @@ -16,6 +16,7 @@ class CPDF_Document; class CPDF_ImageObject; class CPDF_Page; class CPDF_PageObject; +class CPDF_PathObject; class CPDF_PageContentGenerator { public: @@ -25,6 +26,10 @@ class CPDF_PageContentGenerator { void GenerateContent(); private: + friend class cpdf_pagecontentgenerator_ProcessRect_Test; + friend class cpdf_pagecontentgenerator_ProcessPath_Test; + + void ProcessPath(CFX_ByteTextBuf* buf, CPDF_PathObject* pPathObj); void ProcessImage(CFX_ByteTextBuf* buf, CPDF_ImageObject* pImageObj); CFX_ByteString RealizeResource(uint32_t dwResourceObjNum, const CFX_ByteString& bsType); diff --git a/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp new file mode 100644 index 0000000000..8812c0e2b6 --- /dev/null +++ b/core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp @@ -0,0 +1,89 @@ +// Copyright 2017 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h" + +#include "core/fpdfapi/page/cpdf_page.h" +#include "core/fpdfapi/page/cpdf_pathobject.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/base/ptr_util.h" + +TEST(cpdf_pagecontentgenerator, ProcessRect) { + auto pPathObj = pdfium::MakeUnique(); + pPathObj->m_Path.AppendRect(10, 5, 13, 30); + pPathObj->m_FillType = FXFILL_ALTERNATE; + pPathObj->m_bStroke = true; + auto pTestPage = pdfium::MakeUnique(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()); + + pPathObj = pdfium::MakeUnique(); + pPathObj->m_Path.SetPointCount(4); + FX_PATHPOINT* pPoints = pPathObj->m_Path.GetMutablePoints(); + pPoints[0].m_PointX = 0; + pPoints[0].m_PointY = 0; + pPoints[0].m_Flag = FXPT_MOVETO; + pPoints[1].m_PointX = 5.2f; + pPoints[1].m_PointY = 0; + pPoints[1].m_Flag = FXPT_LINETO; + pPoints[2].m_PointX = 5.2f; + pPoints[2].m_PointY = 3.78f; + pPoints[2].m_Flag = FXPT_LINETO; + pPoints[3].m_PointX = 0; + pPoints[3].m_PointY = 3.78f; + pPoints[3].m_Flag = FXPT_LINETO | FXPT_CLOSEFIGURE; + 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()); +} + +TEST(cpdf_pagecontentgenerator, ProcessPath) { + auto pPathObj = pdfium::MakeUnique(); + pPathObj->m_Path.SetPointCount(10); + FX_PATHPOINT* pPoints = pPathObj->m_Path.GetMutablePoints(); + pPoints[0].m_PointX = 3.102f; + pPoints[0].m_PointY = 4.67f; + pPoints[0].m_Flag = FXPT_MOVETO; + pPoints[1].m_PointX = 5.45f; + pPoints[1].m_PointY = 0.29f; + pPoints[1].m_Flag = FXPT_LINETO; + pPoints[2].m_PointX = 4.24f; + pPoints[2].m_PointY = 3.15f; + pPoints[2].m_Flag = FXPT_BEZIERTO; + pPoints[3].m_PointX = 4.65f; + pPoints[3].m_PointY = 2.98f; + pPoints[3].m_Flag = FXPT_BEZIERTO; + pPoints[4].m_PointX = 3.456f; + pPoints[4].m_PointY = 0.24f; + pPoints[4].m_Flag = FXPT_BEZIERTO; + pPoints[5].m_PointX = 10.6f; + pPoints[5].m_PointY = 11.15f; + pPoints[5].m_Flag = FXPT_LINETO; + pPoints[6].m_PointX = 11; + pPoints[6].m_PointY = 12.5f; + pPoints[6].m_Flag = FXPT_LINETO; + pPoints[7].m_PointX = 11.46f; + pPoints[7].m_PointY = 12.67f; + pPoints[7].m_Flag = FXPT_BEZIERTO; + pPoints[8].m_PointX = 11.84f; + pPoints[8].m_PointY = 12.96f; + pPoints[8].m_Flag = FXPT_BEZIERTO; + pPoints[9].m_PointX = 12; + pPoints[9].m_PointY = 13.64f; + pPoints[9].m_Flag = FXPT_BEZIERTO | FXPT_CLOSEFIGURE; + pPathObj->m_FillType = FXFILL_WINDING; + pPathObj->m_bStroke = false; + auto pTestPage = pdfium::MakeUnique(nullptr, nullptr, false); + CPDF_PageContentGenerator generator(pTestPage.get()); + CFX_ByteTextBuf buf; + generator.ProcessPath(&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", + buf.MakeString()); +} -- cgit v1.2.3