From baa7ff4390b8b36c2d58c1b1f8d9775c76da656a Mon Sep 17 00:00:00 2001 From: Jane Liu Date: Thu, 29 Jun 2017 19:18:23 -0400 Subject: Basic APIs and tests for extracting and setting annotation paths 1. Added APIs for retrieving existing annotation paths and setting annotation paths. * Added an embedder test testing all the new functions. Bug=pdfium:737 Change-Id: Ic451bcd3be488261baf2182549c4238b887b219e Reviewed-on: https://pdfium-review.googlesource.com/6676 Commit-Queue: Jane Liu Reviewed-by: Lei Zhang Reviewed-by: dsinclair --- fpdfsdk/fpdfannot.cpp | 174 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 2 deletions(-) (limited to 'fpdfsdk/fpdfannot.cpp') diff --git a/fpdfsdk/fpdfannot.cpp b/fpdfsdk/fpdfannot.cpp index 3d003d738e..4d75e6e236 100644 --- a/fpdfsdk/fpdfannot.cpp +++ b/fpdfsdk/fpdfannot.cpp @@ -9,8 +9,10 @@ #include #include +#include "core/fpdfapi/edit/cpdf_pagecontentgenerator.h" #include "core/fpdfapi/page/cpdf_form.h" #include "core/fpdfapi/page/cpdf_page.h" +#include "core/fpdfapi/page/cpdf_pageobject.h" #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_document.h" @@ -118,6 +120,10 @@ class CPDF_AnnotContext { if (!pStream) return; + // Reset the annotation matrix to be the identity matrix, since the + // appearance stream already takes matrix into account. + pStream->GetDict()->SetMatrixFor("Matrix", CFX_Matrix()); + m_pAnnotForm = pdfium::MakeUnique( m_pPage->m_pDocument.Get(), m_pPage->m_pResources.Get(), pStream); m_pAnnotForm->ParseContent(nullptr, nullptr, nullptr); @@ -125,6 +131,7 @@ class CPDF_AnnotContext { CPDF_Form* GetForm() const { return m_pAnnotForm.get(); } CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); } + CPDF_Page* GetPage() const { return m_pPage.Get(); } private: std::unique_ptr m_pAnnotForm; @@ -144,11 +151,12 @@ bool HasAPStream(const CPDF_Dictionary* pAnnotDict) { DLLEXPORT FPDF_BOOL STDCALL FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) { + // The supported subtypes must also be communicated in the user doc. return subtype == FPDF_ANNOT_CIRCLE || subtype == FPDF_ANNOT_HIGHLIGHT || subtype == FPDF_ANNOT_INK || subtype == FPDF_ANNOT_POPUP || subtype == FPDF_ANNOT_SQUARE || subtype == FPDF_ANNOT_SQUIGGLY || - subtype == FPDF_ANNOT_STRIKEOUT || subtype == FPDF_ANNOT_TEXT || - subtype == FPDF_ANNOT_UNDERLINE; + subtype == FPDF_ANNOT_STAMP || subtype == FPDF_ANNOT_STRIKEOUT || + subtype == FPDF_ANNOT_TEXT || subtype == FPDF_ANNOT_UNDERLINE; } DLLEXPORT FPDF_ANNOTATION STDCALL @@ -228,6 +236,168 @@ FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot) { CPDF_Annot::StringToAnnotSubtype(pAnnotDict->GetStringFor("Subtype"))); } +DLLEXPORT FPDF_BOOL STDCALL FPDFAnnot_UpdatePathObject(FPDF_ANNOTATION annot, + FPDF_PAGEOBJECT path) { + CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot); + CPDF_PageObject* pPathObj = CPDFPageObjectFromFPDFPageObject(path); + if (!pAnnot || !pAnnot->GetAnnotDict() || !pAnnot->HasForm() || !pPathObj || + !pPathObj->IsPath()) + return false; + + // Check that the annotation type is supported by this method. + FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot); + if (subtype != FPDF_ANNOT_INK && subtype != FPDF_ANNOT_STAMP) + return false; + + // Check that the annotation already has an appearance stream, since an + // existing path object is to be updated. + CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(pAnnot->GetAnnotDict(), + CPDF_Annot::AppearanceMode::Normal); + if (!pStream) + return false; + + // Check that the path object is already in this annotation's object list. + CPDF_PageObjectList* pObjList = pAnnot->GetForm()->GetPageObjectList(); + auto it = std::find_if( + pObjList->begin(), pObjList->end(), + [pPathObj](const std::unique_ptr& candidate) { + return candidate.get() == pPathObj; + }); + if (it == pObjList->end()) + return false; + + // Update the content stream data in the annotation's AP stream. + CPDF_PageContentGenerator generator(pAnnot->GetForm()); + std::ostringstream buf; + generator.ProcessPageObjects(&buf); + pStream->SetData(reinterpret_cast(buf.str().c_str()), + buf.tellp()); + return true; +} + +DLLEXPORT FPDF_BOOL STDCALL FPDFAnnot_AppendPathObject(FPDF_ANNOTATION annot, + FPDF_PAGEOBJECT path) { + CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot); + CPDF_PageObject* pPathObj = CPDFPageObjectFromFPDFPageObject(path); + if (!pAnnot || !pPathObj || !pPathObj->IsPath()) + return false; + + CPDF_Dictionary* pAnnotDict = pAnnot->GetAnnotDict(); + CPDF_Page* pPage = pAnnot->GetPage(); + if (!pAnnotDict || !pPage) + return false; + + // Check that the annotation type is supported by this method. + FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot); + if (subtype != FPDF_ANNOT_INK && subtype != FPDF_ANNOT_STAMP) + return false; + + // If the annotation does not have an AP stream yet, generate and set it. + CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(pAnnot->GetAnnotDict(), + CPDF_Annot::AppearanceMode::Normal); + if (!pStream) { + auto pExtGStateDict = + CPVT_GenerateAP::GenerateExtGStateDict(*pAnnotDict, "GS", "Normal"); + auto pResourceDict = CPVT_GenerateAP::GenerateResourceDict( + pPage->m_pDocument.Get(), std::move(pExtGStateDict), nullptr); + std::ostringstream sStream; + CPVT_GenerateAP::GenerateAndSetAPDict(pPage->m_pDocument.Get(), pAnnotDict, + &sStream, std::move(pResourceDict), + false); + pStream = + FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal); + if (!pStream) + return false; + } + + // Get the annotation's corresponding form object for parsing its AP stream. + if (!pAnnot->HasForm()) + pAnnot->SetForm(pStream); + + CPDF_Form* pForm = pAnnot->GetForm(); + + // Check that the path object did not come from the same annotation. If this + // check succeeds, then it is assumed that the path object came from + // FPDFPageObj_CreateNewPath(). Note that a path object that came from a + // different annotation must not be passed here, since a path object cannot + // belong to more than one annotation. + CPDF_PageObjectList* pObjList = pForm->GetPageObjectList(); + auto it = std::find_if( + pObjList->begin(), pObjList->end(), + [pPathObj](const std::unique_ptr& candidate) { + return candidate.get() == pPathObj; + }); + if (it != pObjList->end()) + return false; + + // Append the path object to the object list. + std::unique_ptr pPageObjHolder(pPathObj); + pObjList->push_back(std::move(pPageObjHolder)); + + // Set the content stream data in the annotation's AP stream. + CPDF_PageContentGenerator generator(pForm); + std::ostringstream buf; + generator.ProcessPageObjects(&buf); + pStream->SetData(reinterpret_cast(buf.str().c_str()), + buf.tellp()); + return true; +} + +DLLEXPORT int STDCALL FPDFAnnot_GetPathObjectCount(FPDF_ANNOTATION annot) { + CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot); + if (!pAnnot) + return 0; + + if (!pAnnot->HasForm()) { + CPDF_Stream* pStream = FPDFDOC_GetAnnotAP( + pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal); + if (!pStream) + return 0; + + pAnnot->SetForm(pStream); + } + + int pathCount = 0; + for (const auto& pObj : *pAnnot->GetForm()->GetPageObjectList()) { + if (pObj && pObj->IsPath()) + ++pathCount; + } + return pathCount; +} + +DLLEXPORT FPDF_PAGEOBJECT STDCALL FPDFAnnot_GetPathObject(FPDF_ANNOTATION annot, + int index) { + CPDF_AnnotContext* pAnnot = CPDFAnnotContextFromFPDFAnnotation(annot); + if (!pAnnot || index < 0) + return nullptr; + + if (!pAnnot->HasForm()) { + CPDF_Stream* pStream = FPDFDOC_GetAnnotAP( + pAnnot->GetAnnotDict(), CPDF_Annot::AppearanceMode::Normal); + if (!pStream) + return nullptr; + + pAnnot->SetForm(pStream); + } + + const CPDF_PageObjectList* pObjList = pAnnot->GetForm()->GetPageObjectList(); + if (static_cast(index) >= pObjList->size()) + return nullptr; + + // Retrieve the path object located at |index| in the list of path objects. + // Note that the list of path objects is a sublist of the page object list, + // consisting of only path objects specifically. + int pathCount = -1; + for (const auto& pObj : *pObjList) { + if (pObj && pObj->IsPath()) { + ++pathCount; + if (pathCount == index) + return pObj.get(); + } + } + return nullptr; +} + DLLEXPORT FPDF_BOOL STDCALL FPDFAnnot_SetColor(FPDF_ANNOTATION annot, FPDFANNOT_COLORTYPE type, unsigned int R, -- cgit v1.2.3