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