Add FPDFAnnot_GetInkListPath() API
This is somewhat similar to FPDFAnnot_GetVertices(), but this is for ink
annotations and here the value is an array of paths.
So first add an FPDFAnnot_GetInkListCount() to get the number of paths,
then FPDFAnnot_GetInkListPath() can be used to get the individual paths.
Change-Id: I204a5a53e949fdbb7b264711c27107fe62c9f2be
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/76350
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/constants/annotation_common.h b/constants/annotation_common.h
index 6f96e62..be64206 100644
--- a/constants/annotation_common.h
+++ b/constants/annotation_common.h
@@ -29,6 +29,9 @@
// Entries for polygon and polyline annotations.
constexpr char kVertices[] = "Vertices";
+// Entries for ink annotations
+constexpr char kInkList[] = "InkList";
+
} // namespace annotation
} // namespace pdfium
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index 13c73f6..51b4332 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -296,6 +296,18 @@
return pFormControl ? pForm->GetWidget(pFormControl) : nullptr;
}
+CPDF_Array* GetInkList(FPDF_ANNOTATION annot) {
+ FPDF_ANNOTATION_SUBTYPE subtype = FPDFAnnot_GetSubtype(annot);
+ if (subtype != FPDF_ANNOT_INK)
+ return 0;
+
+ CPDF_Dictionary* annot_dict = GetAnnotDictFromFPDFAnnotation(annot);
+ if (!annot_dict)
+ return 0;
+
+ return annot_dict->GetArrayFor(pdfium::annotation::kInkList);
+}
+
} // namespace
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
@@ -837,6 +849,44 @@
return points_len;
}
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetInkListCount(FPDF_ANNOTATION annot) {
+ CPDF_Array* ink_list = GetInkList(annot);
+ if (!ink_list)
+ return 0;
+
+ return ink_list->size();
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetInkListPath(FPDF_ANNOTATION annot,
+ unsigned long path_index,
+ FS_POINTF* buffer,
+ unsigned long length) {
+ unsigned long path_count = FPDFAnnot_GetInkListCount(annot);
+ if (path_index >= path_count)
+ return 0;
+
+ CPDF_Array* ink_list = GetInkList(annot);
+ if (!ink_list)
+ return 0;
+
+ CPDF_Array* path = ink_list->GetArrayAt(path_index);
+ if (!path)
+ return 0;
+
+ // Truncate to an even number.
+ unsigned long points_len = path->size() / 2;
+ if (buffer && length >= points_len) {
+ for (unsigned long i = 0; i < points_len; ++i) {
+ buffer[i].x = path->GetNumberAt(i * 2);
+ buffer[i].y = path->GetNumberAt(i * 2 + 1);
+ }
+ }
+
+ return points_len;
+}
+
FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFAnnot_HasKey(FPDF_ANNOTATION annot,
FPDF_BYTESTRING key) {
CPDF_Dictionary* pAnnotDict = GetAnnotDictFromFPDFAnnotation(annot);
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 2b66e32..233f842 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -3232,7 +3232,7 @@
ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
ASSERT_TRUE(annot);
- // FPDFSignatureObj_GetTime() positive testing.
+ // FPDFAnnot_GetVertices() positive testing.
unsigned long size = FPDFAnnot_GetVertices(annot.get(), nullptr, 0);
const size_t kExpectedSize = 3;
ASSERT_EQ(kExpectedSize, size);
@@ -3288,3 +3288,92 @@
UnloadPage(page);
}
+
+TEST_F(FPDFAnnotEmbedderTest, InkAnnotation) {
+ ASSERT_TRUE(OpenDocument("ink_annot.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+ EXPECT_EQ(2, FPDFPage_GetAnnotCount(page));
+
+ {
+ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+ ASSERT_TRUE(annot);
+
+ // FPDFAnnot_GetInkListCount() and FPDFAnnot_GetInkListPath() positive
+ // testing.
+ unsigned long size = FPDFAnnot_GetInkListCount(annot.get());
+ const size_t kExpectedSize = 1;
+ ASSERT_EQ(kExpectedSize, size);
+ const unsigned long kPathIndex = 0;
+ unsigned long path_size =
+ FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, nullptr, 0);
+ const size_t kExpectedPathSize = 3;
+ ASSERT_EQ(kExpectedPathSize, path_size);
+ std::vector<FS_POINTF> path_buffer(path_size);
+ EXPECT_EQ(path_size,
+ FPDFAnnot_GetInkListPath(annot.get(), kPathIndex,
+ path_buffer.data(), path_size));
+ EXPECT_FLOAT_EQ(159.0f, path_buffer[0].x);
+ EXPECT_FLOAT_EQ(296.0f, path_buffer[0].y);
+ EXPECT_FLOAT_EQ(350.0f, path_buffer[1].x);
+ EXPECT_FLOAT_EQ(411.0f, path_buffer[1].y);
+ EXPECT_FLOAT_EQ(472.0f, path_buffer[2].x);
+ EXPECT_FLOAT_EQ(243.42f, path_buffer[2].y);
+
+ // FPDFAnnot_GetInkListCount() and FPDFAnnot_GetInkListPath() negative
+ // testing.
+ EXPECT_EQ(0U, FPDFAnnot_GetInkListCount(nullptr));
+ EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(nullptr, 0, nullptr, 0));
+
+ // out of bounds path_index.
+ EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(nullptr, 42, nullptr, 0));
+
+ // path_buffer is not overwritten if it is too small.
+ path_buffer.resize(1);
+ path_buffer[0].x = 42;
+ path_buffer[0].y = 43;
+ path_size = FPDFAnnot_GetInkListPath(
+ annot.get(), kPathIndex, path_buffer.data(), path_buffer.size());
+ EXPECT_EQ(kExpectedSize, size);
+ EXPECT_FLOAT_EQ(42, path_buffer[0].x);
+ EXPECT_FLOAT_EQ(43, path_buffer[0].y);
+ }
+
+ {
+ ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 1));
+ ASSERT_TRUE(annot);
+
+ // This has an odd number of elements in the path array, ignore the last
+ // element.
+ unsigned long size = FPDFAnnot_GetInkListCount(annot.get());
+ const size_t kExpectedSize = 1;
+ ASSERT_EQ(kExpectedSize, size);
+ const unsigned long kPathIndex = 0;
+ unsigned long path_size =
+ FPDFAnnot_GetInkListPath(annot.get(), kPathIndex, nullptr, 0);
+ const size_t kExpectedPathSize = 3;
+ ASSERT_EQ(kExpectedPathSize, path_size);
+ std::vector<FS_POINTF> path_buffer(path_size);
+ EXPECT_EQ(path_size,
+ FPDFAnnot_GetInkListPath(annot.get(), kPathIndex,
+ path_buffer.data(), path_size));
+ EXPECT_FLOAT_EQ(259.0f, path_buffer[0].x);
+ EXPECT_FLOAT_EQ(396.0f, path_buffer[0].y);
+ EXPECT_FLOAT_EQ(450.0f, path_buffer[1].x);
+ EXPECT_FLOAT_EQ(511.0f, path_buffer[1].y);
+ EXPECT_FLOAT_EQ(572.0f, path_buffer[2].x);
+ EXPECT_FLOAT_EQ(343.0f, path_buffer[2].y);
+ }
+
+ {
+ // Wrong annotation type.
+ ScopedFPDFAnnotation polygon_annot(
+ FPDFPage_CreateAnnot(page, FPDF_ANNOT_POLYGON));
+ EXPECT_EQ(0U, FPDFAnnot_GetInkListCount(polygon_annot.get()));
+ const unsigned long kPathIndex = 0;
+ EXPECT_EQ(0U, FPDFAnnot_GetInkListPath(polygon_annot.get(), kPathIndex,
+ nullptr, 0));
+ }
+
+ UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index f3155d6..b47b04f 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -59,6 +59,8 @@
CHK(FPDFAnnot_GetFormFieldName);
CHK(FPDFAnnot_GetFormFieldType);
CHK(FPDFAnnot_GetFormFieldValue);
+ CHK(FPDFAnnot_GetInkListCount);
+ CHK(FPDFAnnot_GetInkListPath);
CHK(FPDFAnnot_GetLink);
CHK(FPDFAnnot_GetLinkedAnnot);
CHK(FPDFAnnot_GetNumberValue);
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index 7159602..d121344 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -412,6 +412,34 @@
unsigned long length);
// Experimental API.
+// Get the number of paths in the ink list of an ink annotation.
+//
+// annot - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot()
+//
+// Returns the number of paths in the ink list if the annotation is of type ink,
+// 0 otherwise.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetInkListCount(FPDF_ANNOTATION annot);
+
+// Experimental API.
+// Get a path in the ink list of an ink annotation. |buffer| is an array of
+// points of the path. If |length| is less than the returned length, or |annot|
+// or |buffer| is NULL, |buffer| will not be modified.
+//
+// annot - handle to an annotation, as returned by e.g. FPDFPage_GetAnnot()
+// path_index - index of the path
+// buffer - buffer for holding the points.
+// length - length of the buffer in points.
+//
+// Returns the number of points of the path if the annotation is of type ink, 0
+// otherwise.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFAnnot_GetInkListPath(FPDF_ANNOTATION annot,
+ unsigned long path_index,
+ FS_POINTF* buffer,
+ unsigned long length);
+
+// Experimental API.
// Check if |annot|'s dictionary has |key| as a key.
//
// annot - handle to an annotation.
diff --git a/testing/resources/ink_annot.in b/testing/resources/ink_annot.in
new file mode 100644
index 0000000..da90b29
--- /dev/null
+++ b/testing/resources/ink_annot.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 4 0 R 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Ink
+ /NM (Ink-1)
+ /F 4
+ /InkList [[159 296 350 411 472 243.42]]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Ink
+ /NM (Ink-2)
+ /F 4
+ /InkList [[259 396 450 511 572 343 42]]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/ink_annot.pdf b/testing/resources/ink_annot.pdf
new file mode 100644
index 0000000..306f28d
--- /dev/null
+++ b/testing/resources/ink_annot.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 4 0 R 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Ink
+ /NM (Ink-1)
+ /F 4
+ /InkList [[159 296 350 411 472 243.42]]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+5 0 obj <<
+ /Type /Annot
+ /Subtype /Ink
+ /NM (Ink-2)
+ /F 4
+ /InkList [[259 396 450 511 572 343 42]]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000251 00000 n
+0000000422 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+593
+%%EOF