summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRalf Sippl <ralf.sippl@gmail.com>2018-04-12 21:20:26 +0000
committerChromium commit bot <commit-bot@chromium.org>2018-04-12 21:20:26 +0000
commit1638179e85863b5045fcea2282fd3e0622aeac13 (patch)
treee21cc4c409ed04ccb4496f17c4edcc0d8f586cdc
parentc9f4f0dbcaaf9e86ef2b64c844b094bd2f6b1a92 (diff)
downloadpdfium-1638179e85863b5045fcea2282fd3e0622aeac13.tar.xz
Add index parameter to quadpoints getter and setter.
This is needed for working with multiline text markup annotations. Based on https://pdfium-review.googlesource.com/12012. Bug: pdfium:1045 Change-Id: Ifb105996b8b950bb2d5fceaf754b4f571155aef4 Reviewed-on: https://pdfium-review.googlesource.com/29150 Commit-Queue: Lei Zhang <thestig@chromium.org> Reviewed-by: Lei Zhang <thestig@chromium.org> Reviewed-by: Henrique Nakashima <hnakashima@chromium.org>
-rw-r--r--core/fpdfdoc/cpdf_annot.cpp2
-rw-r--r--core/fpdfdoc/cpdf_annot.h3
-rw-r--r--fpdfsdk/cpdfsdk_helpers.cpp35
-rw-r--r--fpdfsdk/cpdfsdk_helpers.h10
-rw-r--r--fpdfsdk/fpdf_annot.cpp108
-rw-r--r--fpdfsdk/fpdf_annot_embeddertest.cpp99
-rw-r--r--fpdfsdk/fpdf_doc.cpp15
-rw-r--r--fpdfsdk/fpdf_view_c_api_test.c1
-rw-r--r--public/fpdf_annot.h28
-rw-r--r--samples/pdfium_test_write_helper.cc25
10 files changed, 265 insertions, 61 deletions
diff --git a/core/fpdfdoc/cpdf_annot.cpp b/core/fpdfdoc/cpdf_annot.cpp
index 42bda6e319..ef803c428d 100644
--- a/core/fpdfdoc/cpdf_annot.cpp
+++ b/core/fpdfdoc/cpdf_annot.cpp
@@ -229,7 +229,7 @@ CFX_FloatRect CPDF_Annot::RectFromQuadPointsArray(const CPDF_Array* pArray,
// static
CFX_FloatRect CPDF_Annot::BoundingRectFromQuadPoints(
- CPDF_Dictionary* pAnnotDict) {
+ const CPDF_Dictionary* pAnnotDict) {
CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
if (!pArray)
return CFX_FloatRect();
diff --git a/core/fpdfdoc/cpdf_annot.h b/core/fpdfdoc/cpdf_annot.h
index 3f1164ad18..b2875949a3 100644
--- a/core/fpdfdoc/cpdf_annot.h
+++ b/core/fpdfdoc/cpdf_annot.h
@@ -69,7 +69,8 @@ class CPDF_Annot {
static ByteString AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype);
static CFX_FloatRect RectFromQuadPointsArray(const CPDF_Array* pArray,
size_t nIndex);
- static CFX_FloatRect BoundingRectFromQuadPoints(CPDF_Dictionary* pAnnotDict);
+ static CFX_FloatRect BoundingRectFromQuadPoints(
+ const CPDF_Dictionary* pAnnotDict);
static CFX_FloatRect RectFromQuadPoints(CPDF_Dictionary* pAnnotDict,
size_t nIndex);
static size_t QuadPointCount(const CPDF_Array* pArray);
diff --git a/fpdfsdk/cpdfsdk_helpers.cpp b/fpdfsdk/cpdfsdk_helpers.cpp
index 45c2674a4d..19252f7c5b 100644
--- a/fpdfsdk/cpdfsdk_helpers.cpp
+++ b/fpdfsdk/cpdfsdk_helpers.cpp
@@ -19,6 +19,8 @@
namespace {
+constexpr char kQuadPoints[] = "QuadPoints";
+
FPDF_DOCUMENT FPDFDocumentFromUnderlying(UnderlyingDocumentType* doc) {
return static_cast<FPDF_DOCUMENT>(doc);
}
@@ -429,10 +431,41 @@ CFX_FloatRect CFXFloatRectFromFSRECTF(const FS_RECTF& rect) {
return CFX_FloatRect(rect.left, rect.bottom, rect.right, rect.top);
}
-const CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict) {
+CPDF_Array* GetQuadPointsArrayFromDictionary(const CPDF_Dictionary* dict) {
return dict ? dict->GetArrayFor("QuadPoints") : nullptr;
}
+CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict) {
+ if (!dict)
+ return nullptr;
+ return dict->SetNewFor<CPDF_Array>(kQuadPoints);
+}
+
+bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index) {
+ return array && index < array->GetCount() / 8;
+}
+
+bool GetQuadPointsAtIndex(const CPDF_Array* array,
+ size_t quad_index,
+ FS_QUADPOINTSF* quad_points) {
+ ASSERT(quad_points);
+ ASSERT(array);
+
+ if (!IsValidQuadPointsIndex(array, quad_index))
+ return false;
+
+ quad_index *= 8;
+ quad_points->x1 = array->GetNumberAt(quad_index);
+ quad_points->y1 = array->GetNumberAt(quad_index + 1);
+ quad_points->x2 = array->GetNumberAt(quad_index + 2);
+ quad_points->y2 = array->GetNumberAt(quad_index + 3);
+ quad_points->x3 = array->GetNumberAt(quad_index + 4);
+ quad_points->y3 = array->GetNumberAt(quad_index + 5);
+ quad_points->x4 = array->GetNumberAt(quad_index + 6);
+ quad_points->y4 = array->GetNumberAt(quad_index + 7);
+ return true;
+}
+
bool GetQuadPointsFromDictionary(CPDF_Dictionary* dict,
size_t quad_index,
FS_QUADPOINTSF* quad_points) {
diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h
index dcd0cc9558..a5e03292a0 100644
--- a/fpdfsdk/cpdfsdk_helpers.h
+++ b/fpdfsdk/cpdfsdk_helpers.h
@@ -63,10 +63,12 @@ RetainPtr<IFX_SeekableStream> MakeSeekableStream(
FPDF_FILEHANDLER* pFileHandler);
#endif // PDF_ENABLE_XFA
-const CPDF_Array* GetQuadPointsArrayFromDictionary(CPDF_Dictionary* dict);
-bool GetQuadPointsFromDictionary(CPDF_Dictionary* dict,
- size_t quad_index,
- FS_QUADPOINTSF* quad_points);
+CPDF_Array* GetQuadPointsArrayFromDictionary(const CPDF_Dictionary* dict);
+CPDF_Array* AddQuadPointsArrayToDictionary(CPDF_Dictionary* dict);
+bool IsValidQuadPointsIndex(const CPDF_Array* array, size_t index);
+bool GetQuadPointsAtIndex(const CPDF_Array* array,
+ size_t quad_index,
+ FS_QUADPOINTSF* quad_points);
CFX_FloatRect CFXFloatRectFromFSRECTF(const FS_RECTF& rect);
void FSRECTFFromCFXFloatRect(const CFX_FloatRect& rect, FS_RECTF* out_rect);
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index 2ab0bca454..ccc7596401 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -194,6 +194,51 @@ void UpdateContentStream(CPDF_Form* pForm, CPDF_Stream* pStream) {
pStream->SetDataAndRemoveFilter(&buf);
}
+void SetQuadPointsAtIndex(CPDF_Array* array,
+ size_t quad_index,
+ const FS_QUADPOINTSF* quad_points) {
+ ASSERT(array);
+ ASSERT(quad_points);
+ ASSERT(IsValidQuadPointsIndex(array, quad_index));
+
+ size_t nIndex = quad_index * 8;
+ array->SetNewAt<CPDF_Number>(nIndex, quad_points->x1);
+ array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y1);
+ array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x2);
+ array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y2);
+ array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x3);
+ array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y3);
+ array->SetNewAt<CPDF_Number>(++nIndex, quad_points->x4);
+ array->SetNewAt<CPDF_Number>(++nIndex, quad_points->y4);
+}
+
+void AppendQuadPoints(CPDF_Array* array, const FS_QUADPOINTSF* quad_points) {
+ ASSERT(quad_points);
+ ASSERT(array);
+
+ array->AddNew<CPDF_Number>(quad_points->x1);
+ array->AddNew<CPDF_Number>(quad_points->y1);
+ array->AddNew<CPDF_Number>(quad_points->x2);
+ array->AddNew<CPDF_Number>(quad_points->y2);
+ array->AddNew<CPDF_Number>(quad_points->x3);
+ array->AddNew<CPDF_Number>(quad_points->y3);
+ array->AddNew<CPDF_Number>(quad_points->x4);
+ array->AddNew<CPDF_Number>(quad_points->y4);
+}
+
+void UpdateBBox(const CPDF_Dictionary* annot_dict) {
+ // Update BBox entry in appearance stream based on the bounding rectangle
+ // of the annotation's quadpoints.
+ CPDF_Stream* pStream =
+ FPDFDOC_GetAnnotAP(annot_dict, CPDF_Annot::AppearanceMode::Normal);
+ if (pStream) {
+ CFX_FloatRect boundingRect =
+ CPDF_Annot::BoundingRectFromQuadPoints(annot_dict);
+ if (boundingRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
+ pStream->GetDict()->SetRectFor("BBox", boundingRect);
+ }
+}
+
} // namespace
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -582,42 +627,35 @@ FPDFAnnot_HasAttachmentPoints(FPDF_ANNOTATION annot) {
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot,
+ size_t quad_index,
const FS_QUADPOINTSF* quad_points) {
if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
return false;
CPDF_Dictionary* pAnnotDict =
CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
- if (!pAnnotDict)
+ CPDF_Array* pQuadPointsArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+ if (!IsValidQuadPointsIndex(pQuadPointsArray, quad_index))
return false;
- // Update the "QuadPoints" entry in the annotation dictionary.
- CPDF_Array* pQuadPoints = pAnnotDict->GetArrayFor("QuadPoints");
- if (pQuadPoints)
- pQuadPoints->Clear();
- else
- pQuadPoints = pAnnotDict->SetNewFor<CPDF_Array>("QuadPoints");
-
- pQuadPoints->AddNew<CPDF_Number>(quad_points->x1);
- pQuadPoints->AddNew<CPDF_Number>(quad_points->y1);
- pQuadPoints->AddNew<CPDF_Number>(quad_points->x2);
- pQuadPoints->AddNew<CPDF_Number>(quad_points->y2);
- pQuadPoints->AddNew<CPDF_Number>(quad_points->x3);
- pQuadPoints->AddNew<CPDF_Number>(quad_points->y3);
- pQuadPoints->AddNew<CPDF_Number>(quad_points->x4);
- pQuadPoints->AddNew<CPDF_Number>(quad_points->y4);
-
- // If the annotation's appearance stream is defined, and the new quadpoints
- // defines a bigger bounding box than the appearance stream currently
- // specifies, then update the "BBox" entry in the AP dictionary too, since it
- // comes from annotation dictionary's "QuadPoints" entry.
- CPDF_Stream* pStream =
- FPDFDOC_GetAnnotAP(pAnnotDict, CPDF_Annot::AppearanceMode::Normal);
- if (pStream) {
- CFX_FloatRect newRect = CPDF_Annot::BoundingRectFromQuadPoints(pAnnotDict);
- if (newRect.Contains(pStream->GetDict()->GetRectFor("BBox")))
- pStream->GetDict()->SetRectFor("BBox", newRect);
- }
+ SetQuadPointsAtIndex(pQuadPointsArray, quad_index, quad_points);
+ UpdateBBox(pAnnotDict);
+ return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_AppendAttachmentPoints(FPDF_ANNOTATION annot,
+ const FS_QUADPOINTSF* quad_points) {
+ if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
+ return false;
+
+ CPDF_Dictionary* pAnnotDict =
+ CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
+ CPDF_Array* pQuadPointsArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+ if (!pQuadPointsArray)
+ pQuadPointsArray = AddQuadPointsArrayToDictionary(pAnnotDict);
+ AppendQuadPoints(pQuadPointsArray, quad_points);
+ UpdateBBox(pAnnotDict);
return true;
}
@@ -634,13 +672,21 @@ FPDFAnnot_CountAttachmentPoints(FPDF_ANNOTATION annot) {
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot,
+ size_t quad_index,
FS_QUADPOINTSF* quad_points) {
if (!FPDFAnnot_HasAttachmentPoints(annot) || !quad_points)
return false;
- return GetQuadPointsFromDictionary(
- CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict(), 0,
- quad_points);
+ CPDF_Dictionary* pAnnotDict =
+ CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
+ if (!pAnnotDict)
+ return false;
+
+ const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pAnnotDict);
+ if (!pArray)
+ return false;
+
+ return GetQuadPointsAtIndex(pArray, quad_index, quad_points);
}
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_SetRect(FPDF_ANNOTATION annot,
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index b97193db46..9d5c5548b6 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -134,7 +134,7 @@ TEST_F(FPDFAnnotEmbeddertest, ExtractHighlightLongContent) {
// Check that the quadpoints are correct.
FS_QUADPOINTSF quadpoints;
- ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), &quadpoints));
+ ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
EXPECT_EQ(115.802643f, quadpoints.x1);
EXPECT_EQ(718.913940f, quadpoints.y1);
EXPECT_EQ(157.211182f, quadpoints.x4);
@@ -303,7 +303,7 @@ TEST_F(FPDFAnnotEmbeddertest, AddAndSaveUnderlineAnnotation) {
std::unique_ptr<void, FPDFAnnotationDeleter> annot(
FPDFPage_GetAnnot(page, 0));
ASSERT_TRUE(annot);
- ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), &quadpoints));
+ ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
EXPECT_EQ(115.802643f, quadpoints.x1);
EXPECT_EQ(718.913940f, quadpoints.y1);
EXPECT_EQ(157.211182f, quadpoints.x4);
@@ -317,7 +317,7 @@ TEST_F(FPDFAnnotEmbeddertest, AddAndSaveUnderlineAnnotation) {
ASSERT_TRUE(annot);
quadpoints.x1 = 140.802643f;
quadpoints.x3 = 140.802643f;
- ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot.get(), &quadpoints));
+ ASSERT_TRUE(FPDFAnnot_AppendAttachmentPoints(annot.get(), &quadpoints));
}
// Save the document, closing the page and document.
@@ -343,7 +343,7 @@ TEST_F(FPDFAnnotEmbeddertest, AddAndSaveUnderlineAnnotation) {
EXPECT_EQ(FPDF_ANNOT_UNDERLINE, FPDFAnnot_GetSubtype(new_annot.get()));
FS_QUADPOINTSF new_quadpoints;
ASSERT_TRUE(
- FPDFAnnot_GetAttachmentPoints(new_annot.get(), &new_quadpoints));
+ FPDFAnnot_GetAttachmentPoints(new_annot.get(), 0, &new_quadpoints));
EXPECT_NEAR(quadpoints.x1, new_quadpoints.x1, 0.001f);
EXPECT_NEAR(quadpoints.y1, new_quadpoints.y1, 0.001f);
EXPECT_NEAR(quadpoints.x4, new_quadpoints.x4, 0.001f);
@@ -354,6 +354,91 @@ TEST_F(FPDFAnnotEmbeddertest, AddAndSaveUnderlineAnnotation) {
CloseSavedDocument();
}
+TEST_F(FPDFAnnotEmbeddertest, GetAndSetQuadPoints) {
+ // Open a file with four annotations and load its first page.
+ ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+ EXPECT_EQ(4, FPDFPage_GetAnnotCount(page));
+
+ // Retrieve the highlight annotation.
+ FPDF_ANNOTATION annot = FPDFPage_GetAnnot(page, 0);
+ ASSERT_TRUE(annot);
+ ASSERT_EQ(FPDF_ANNOT_HIGHLIGHT, FPDFAnnot_GetSubtype(annot));
+
+ FS_QUADPOINTSF quadpoints;
+ ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 0, &quadpoints));
+
+ {
+ // Verify the current one set of quadpoints.
+ ASSERT_EQ(1u, FPDFAnnot_CountAttachmentPoints(annot));
+
+ EXPECT_NEAR(72.0000f, quadpoints.x1, 0.001f);
+ EXPECT_NEAR(720.792f, quadpoints.y1, 0.001f);
+ EXPECT_NEAR(132.055f, quadpoints.x4, 0.001f);
+ EXPECT_NEAR(704.796f, quadpoints.y4, 0.001f);
+ }
+
+ {
+ // Update the quadpoints.
+ FS_QUADPOINTSF new_quadpoints = quadpoints;
+ new_quadpoints.y1 -= 20.f;
+ new_quadpoints.y2 -= 20.f;
+ new_quadpoints.y3 -= 20.f;
+ new_quadpoints.y4 -= 20.f;
+ ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot, 0, &new_quadpoints));
+
+ // Verify added quadpoint set
+ ASSERT_EQ(1u, FPDFAnnot_CountAttachmentPoints(annot));
+ ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 0, &quadpoints));
+ EXPECT_NEAR(new_quadpoints.x1, quadpoints.x1, 0.001f);
+ EXPECT_NEAR(new_quadpoints.y1, quadpoints.y1, 0.001f);
+ EXPECT_NEAR(new_quadpoints.x4, quadpoints.x4, 0.001f);
+ EXPECT_NEAR(new_quadpoints.y4, quadpoints.y4, 0.001f);
+ }
+
+ {
+ // Append a new set of quadpoints.
+ FS_QUADPOINTSF new_quadpoints = quadpoints;
+ new_quadpoints.y1 += 20.f;
+ new_quadpoints.y2 += 20.f;
+ new_quadpoints.y3 += 20.f;
+ new_quadpoints.y4 += 20.f;
+ ASSERT_TRUE(FPDFAnnot_AppendAttachmentPoints(annot, &new_quadpoints));
+
+ // Verify added quadpoint set
+ ASSERT_EQ(2u, FPDFAnnot_CountAttachmentPoints(annot));
+ ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot, 1, &quadpoints));
+ EXPECT_NEAR(new_quadpoints.x1, quadpoints.x1, 0.001f);
+ EXPECT_NEAR(new_quadpoints.y1, quadpoints.y1, 0.001f);
+ EXPECT_NEAR(new_quadpoints.x4, quadpoints.x4, 0.001f);
+ EXPECT_NEAR(new_quadpoints.y4, quadpoints.y4, 0.001f);
+ }
+
+ {
+ // Setting and getting quadpoints at out-of-bound index should fail
+ EXPECT_FALSE(FPDFAnnot_SetAttachmentPoints(annot, 300000, &quadpoints));
+ EXPECT_FALSE(FPDFAnnot_GetAttachmentPoints(annot, 300000, &quadpoints));
+ }
+
+ FPDFPage_CloseAnnot(annot);
+
+ // Retrieve the square annotation
+ FPDF_ANNOTATION squareAnnot = FPDFPage_GetAnnot(page, 2);
+
+ {
+ // Check that attempting to set its quadpoints would fail
+ ASSERT_TRUE(squareAnnot);
+ EXPECT_EQ(FPDF_ANNOT_SQUARE, FPDFAnnot_GetSubtype(squareAnnot));
+ EXPECT_EQ(0u, FPDFAnnot_CountAttachmentPoints(squareAnnot));
+ EXPECT_FALSE(FPDFAnnot_SetAttachmentPoints(squareAnnot, 0, &quadpoints));
+ }
+
+ FPDFPage_CloseAnnot(squareAnnot);
+
+ UnloadPage(page);
+}
+
TEST_F(FPDFAnnotEmbeddertest, ModifyRectQuadpointsWithAP) {
#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
const char md5_original[] = "63af8432fab95a67cdebb7cd0e514941";
@@ -398,7 +483,7 @@ TEST_F(FPDFAnnotEmbeddertest, ModifyRectQuadpointsWithAP) {
// Verify its attachment points.
FS_QUADPOINTSF quadpoints;
- ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), &quadpoints));
+ ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &quadpoints));
EXPECT_NEAR(72.0000f, quadpoints.x1, 0.001f);
EXPECT_NEAR(720.792f, quadpoints.y1, 0.001f);
EXPECT_NEAR(132.055f, quadpoints.x4, 0.001f);
@@ -409,9 +494,9 @@ TEST_F(FPDFAnnotEmbeddertest, ModifyRectQuadpointsWithAP) {
quadpoints.x2 -= 50.f;
quadpoints.x3 -= 50.f;
quadpoints.x4 -= 50.f;
- ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot.get(), &quadpoints));
+ ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot.get(), 0, &quadpoints));
FS_QUADPOINTSF new_quadpoints;
- ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), &new_quadpoints));
+ ASSERT_TRUE(FPDFAnnot_GetAttachmentPoints(annot.get(), 0, &new_quadpoints));
EXPECT_EQ(quadpoints.x1, new_quadpoints.x1);
EXPECT_EQ(quadpoints.y1, new_quadpoints.y1);
EXPECT_EQ(quadpoints.x4, new_quadpoints.x4);
diff --git a/fpdfsdk/fpdf_doc.cpp b/fpdfsdk/fpdf_doc.cpp
index d8be12066a..54718a9409 100644
--- a/fpdfsdk/fpdf_doc.cpp
+++ b/fpdfsdk/fpdf_doc.cpp
@@ -12,6 +12,7 @@
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfdoc/cpdf_bookmark.h"
#include "core/fpdfdoc/cpdf_bookmarktree.h"
#include "core/fpdfdoc/cpdf_dest.h"
@@ -390,9 +391,17 @@ FPDFLink_GetQuadPoints(FPDF_LINK link_annot,
FS_QUADPOINTSF* quad_points) {
if (!quad_points || quad_index < 0)
return false;
- return GetQuadPointsFromDictionary(CPDFDictionaryFromFPDFLink(link_annot),
- static_cast<size_t>(quad_index),
- quad_points);
+
+ CPDF_Dictionary* pLinkDict = CPDFDictionaryFromFPDFLink(link_annot);
+ if (!pLinkDict)
+ return false;
+
+ const CPDF_Array* pArray = GetQuadPointsArrayFromDictionary(pLinkDict);
+ if (!pArray)
+ return false;
+
+ return GetQuadPointsAtIndex(pArray, static_cast<size_t>(quad_index),
+ quad_points);
}
FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document,
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 1942b73957..fbfa71cf70 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -55,6 +55,7 @@ int CheckPDFiumCApi() {
CHK(FPDFAnnot_GetColor);
CHK(FPDFAnnot_HasAttachmentPoints);
CHK(FPDFAnnot_SetAttachmentPoints);
+ CHK(FPDFAnnot_AppendAttachmentPoints);
CHK(FPDFAnnot_CountAttachmentPoints);
CHK(FPDFAnnot_GetAttachmentPoints);
CHK(FPDFAnnot_SetRect);
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index a84c4f982a..8e9b93e12d 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -285,7 +285,7 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_GetColor(FPDF_ANNOTATION annot,
// Experimental API.
// Check if the annotation is of a type that has attachment points
-// (i.e. quadpoints). Quadpoints are the vertices of the rectange that
+// (i.e. quadpoints). Quadpoints are the vertices of the rectangle that
// encompasses the texts affected by the annotation. They provide the
// coordinates in the page where the annotation is attached. Only text markup
// annotations (i.e. highlight, strikeout, squiggly, and underline) and link
@@ -299,20 +299,38 @@ FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_HasAttachmentPoints(FPDF_ANNOTATION annot);
// Experimental API.
-// Set the attachment points (i.e. quadpoints) of an annotation. If the
-// annotation's appearance stream is defined and this annotation is of a type
-// with quadpoints, then update the bounding box too if the new quadpoints
+// Replace the attachment points (i.e. quadpoints) set of an annotation at
+// |quad_index|. This index needs to be within the result of
+// FPDFAnnot_CountAttachmentPoints().
+// If the annotation's appearance stream is defined and this annotation is of a
+// type with quadpoints, then update the bounding box too if the new quadpoints
// define a bigger one.
//
// annot - handle to an annotation.
+// quad_index - index of the set of quadpoints.
// quad_points - the quadpoints to be set.
//
// Returns true if successful.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_SetAttachmentPoints(FPDF_ANNOTATION annot,
+ size_t quad_index,
const FS_QUADPOINTSF* quad_points);
// Experimental API.
+// Append to the list of attachment points (i.e. quadpoints) of an annotation.
+// If the annotation's appearance stream is defined and this annotation is of a
+// type with quadpoints, then update the bounding box too if the new quadpoints
+// define a bigger one.
+//
+// annot - handle to an annotation.
+// quad_points - the quadpoints to be set.
+//
+// Returns true if successful.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_AppendAttachmentPoints(FPDF_ANNOTATION annot,
+ const FS_QUADPOINTSF* quad_points);
+
+// Experimental API.
// Get the number of sets of quadpoints of an annotation.
//
// annot - handle to an annotation.
@@ -325,11 +343,13 @@ FPDFAnnot_CountAttachmentPoints(FPDF_ANNOTATION annot);
// Get the attachment points (i.e. quadpoints) of an annotation.
//
// annot - handle to an annotation.
+// quad_index - index of the set of quadpoints.
// quad_points - receives the quadpoints; must not be NULL.
//
// Returns true if successful.
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFAnnot_GetAttachmentPoints(FPDF_ANNOTATION annot,
+ size_t quad_index,
FS_QUADPOINTSF* quad_points);
// Experimental API.
diff --git a/samples/pdfium_test_write_helper.cc b/samples/pdfium_test_write_helper.cc
index 5a2e0f66ce..d061252e07 100644
--- a/samples/pdfium_test_write_helper.cc
+++ b/samples/pdfium_test_write_helper.cc
@@ -315,15 +315,22 @@ void WriteAnnot(FPDF_PAGE page, const char* pdf_name, int num) {
// Retrieve the annotation's quadpoints if it is a markup annotation.
if (FPDFAnnot_HasAttachmentPoints(annot.get())) {
- FS_QUADPOINTSF quadpoints;
- if (FPDFAnnot_GetAttachmentPoints(annot.get(), &quadpoints)) {
- fprintf(fp,
- "Quadpoints: (%.3f, %.3f), (%.3f, %.3f), (%.3f, %.3f), (%.3f, "
- "%.3f)\n",
- quadpoints.x1, quadpoints.y1, quadpoints.x2, quadpoints.y2,
- quadpoints.x3, quadpoints.y3, quadpoints.x4, quadpoints.y4);
- } else {
- fprintf(fp, "Failed to retrieve quadpoints.\n");
+ size_t qp_count = FPDFAnnot_CountAttachmentPoints(annot.get());
+ fprintf(fp, "Number of quadpoints sets: %zu\n", qp_count);
+
+ // Iterate through all quadpoints of the current annotation
+ for (size_t j = 0; j < qp_count; ++j) {
+ FS_QUADPOINTSF quadpoints;
+ if (FPDFAnnot_GetAttachmentPoints(annot.get(), j, &quadpoints)) {
+ fprintf(fp,
+ "Quadpoints set #%zu: (%.3f, %.3f), (%.3f, %.3f), "
+ "(%.3f, %.3f), (%.3f, %.3f)\n",
+ j + 1, quadpoints.x1, quadpoints.y1, quadpoints.x2,
+ quadpoints.y2, quadpoints.x3, quadpoints.y3, quadpoints.x4,
+ quadpoints.y4);
+ } else {
+ fprintf(fp, "Failed to retrieve quadpoints set #%zu.\n", j + 1);
+ }
}
}