1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "pdf_document_jni.h"
18
19 #include <android/bitmap.h>
20 #include <assert.h>
21 #include <jni.h>
22 #include <stdio.h>
23 #include <sys/mman.h>
24 #include <unistd.h>
25
26 #include <memory>
27 #include <mutex>
28 #include <string>
29 #include <unordered_set>
30
31 #include "document.h"
32 #include "fcntl.h"
33 #include "file.h"
34 #include "form_widget_info.h"
35 #include "jni_conversion.h"
36 #include "logging.h"
37 #include "page.h"
38 #include "rect.h"
39 // #include "util/java/scoped_local_ref.h"
40 #include <unistd.h>
41
42 #define LOG_TAG "pdf_document_jni"
43
44 using pdfClient::Document;
45 using pdfClient::FileReader;
46 using pdfClient::GotoLink;
47 using pdfClient::Page;
48 using pdfClient::Point_i;
49 using pdfClient::Rectangle_i;
50 using pdfClient::SelectionBoundary;
51 using pdfClient::Status;
52 using std::vector;
53
54 using pdfClient::LinuxFileOps;
55
56 namespace {
57 std::mutex mutex_;
58
59 /** Matrix organizes its values in row-major order. These constants correspond to each
60 * value in Matrix.
61 */
62 constexpr int kMScaleX = 0; // horizontal scale factor
63 constexpr int kMSkewX = 1; // horizontal skew factor
64 constexpr int kMTransX = 2; // horizontal translation
65 constexpr int kMSkewY = 3; // vertical skew factor
66 constexpr int kMScaleY = 4; // vertical scale factor
67 constexpr int kMTransY = 5; // vertical translation
68 constexpr int kMPersp0 = 6; // input x perspective factor
69 constexpr int kMPersp1 = 7; // input y perspective factor
70 constexpr int kMPersp2 = 8; // perspective bias
71 } // namespace
72
JNI_OnLoad(JavaVM * vm,void * reserved)73 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
74 std::unique_lock<std::mutex> lock(mutex_);
75 pdfClient::InitLibrary();
76 // NOTE(olsen): We never call FPDF_DestroyLibrary. Would it add any benefit?
77 return JNI_VERSION_1_6;
78 }
79
Java_android_graphics_pdf_PdfDocumentProxy_createFromFd(JNIEnv * env,jobject obj,jint jfd,jstring jpassword)80 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_createFromFd(
81 JNIEnv* env, jobject obj, jint jfd, jstring jpassword) {
82 std::unique_lock<std::mutex> lock(mutex_);
83 LinuxFileOps::FDCloser fd(jfd);
84 const char* password = jpassword == NULL ? NULL : env->GetStringUTFChars(jpassword, NULL);
85 LOGD("Creating FPDF_DOCUMENT from fd: %d", fd.get());
86 std::unique_ptr<Document> doc;
87
88 auto fileReader = std::make_unique<FileReader>(std::move(fd));
89 size_t pdfSizeInBytes = fileReader->CompleteSize();
90 Status status = Document::Load(std::move(fileReader), password,
91 /* closeFdOnFailure= */ true, &doc);
92
93 if (password) {
94 env->ReleaseStringUTFChars(jpassword, password);
95 }
96 // doc is owned by the LoadPdfResult in java.
97 return convert::ToJavaLoadPdfResult(env, status, std::move(doc), pdfSizeInBytes);
98 }
99
Java_android_graphics_pdf_PdfDocumentProxy_destroy(JNIEnv * env,jobject jPdfDocument)100 JNIEXPORT void JNICALL Java_android_graphics_pdf_PdfDocumentProxy_destroy(JNIEnv* env,
101 jobject jPdfDocument) {
102 std::unique_lock<std::mutex> lock(mutex_);
103 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
104 LOGD("Deleting Document: %p", doc);
105 delete doc;
106 LOGD("Destroyed Document: %p", doc);
107 }
108
Java_android_graphics_pdf_PdfDocumentProxy_saveToFd(JNIEnv * env,jobject jPdfDocument,jint jfd)109 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_saveToFd(JNIEnv* env,
110 jobject jPdfDocument,
111 jint jfd) {
112 std::unique_lock<std::mutex> lock(mutex_);
113 LinuxFileOps::FDCloser fd(jfd);
114 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
115 LOGD("Saving Document %p to fd %d", doc, fd.get());
116 return doc->SaveAs(std::move(fd));
117 }
118
119 // TODO(b/321979602): Cleanup Dimensions, reusing `android.util.Size`
Java_android_graphics_pdf_PdfDocumentProxy_getPageDimensions(JNIEnv * env,jobject jPdfDocument,jint pageNum)120 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageDimensions(
121 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
122 std::unique_lock<std::mutex> lock(mutex_);
123 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
124 std::shared_ptr<Page> page = doc->GetPage(pageNum);
125 Rectangle_i dimensions = page->Dimensions();
126 if (pdfClient::IsEmpty(dimensions)) {
127 LOGE("pdfClient returned 0x0 page dimensions for page %d", pageNum);
128 dimensions = pdfClient::IntRect(0, 0, 612, 792); // Default to Letter size.
129 }
130 return convert::ToJavaDimensions(env, dimensions);
131 }
132
Java_android_graphics_pdf_PdfDocumentProxy_getPageWidth(JNIEnv * env,jobject jPdfDocument,jint pageNum)133 JNIEXPORT jint JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageWidth(JNIEnv* env,
134 jobject jPdfDocument,
135 jint pageNum) {
136 std::unique_lock<std::mutex> lock(mutex_);
137 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
138 std::shared_ptr<Page> page = doc->GetPage(pageNum);
139 return page->Width();
140 }
141
Java_android_graphics_pdf_PdfDocumentProxy_getPageHeight(JNIEnv * env,jobject jPdfDocument,jint pageNum)142 JNIEXPORT jint JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageHeight(JNIEnv* env,
143 jobject jPdfDocument,
144 jint pageNum) {
145 std::unique_lock<std::mutex> lock(mutex_);
146 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
147 std::shared_ptr<Page> page = doc->GetPage(pageNum);
148 return page->Height();
149 }
150
Java_android_graphics_pdf_PdfDocumentProxy_render(JNIEnv * env,jobject jPdfDocument,jint pageNum,jobject jbitmap,jint clipLeft,jint clipTop,jint clipRight,jint clipBottom,jfloatArray jTransform,jint renderMode,jint showAnnotTypes,jboolean renderFormFields)151 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_render(
152 JNIEnv* env, jobject jPdfDocument, jint pageNum, jobject jbitmap, jint clipLeft,
153 jint clipTop, jint clipRight, jint clipBottom, jfloatArray jTransform, jint renderMode,
154 jint showAnnotTypes, jboolean renderFormFields) {
155 std::unique_lock<std::mutex> lock(mutex_);
156 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
157
158 // android.graphics.Bitmap -> FPDF_Bitmap
159 void* bitmap_pixels;
160 if (AndroidBitmap_lockPixels(env, jbitmap, &bitmap_pixels) < 0) {
161 LOGE("Couldn't get bitmap pixel address");
162 return false;
163 }
164 AndroidBitmapInfo info;
165 AndroidBitmap_getInfo(env, jbitmap, &info);
166 const int stride = info.width * 4;
167 FPDF_BITMAP bitmap =
168 FPDFBitmap_CreateEx(info.width, info.height, FPDFBitmap_BGRA, bitmap_pixels, stride);
169
170 // android.graphics.Matrix (SkMatrix) -> FS_Matrix
171 float transform[9];
172 env->GetFloatArrayRegion(jTransform, 0, 9, transform);
173 if (transform[kMPersp0] != 0 || transform[kMPersp1] != 0 || transform[kMPersp2] != 1) {
174 LOGE("Non-affine transform provided");
175 return false;
176 }
177
178 FS_MATRIX pdfiumTransform = {transform[kMScaleX], transform[kMSkewY], transform[kMSkewX],
179 transform[kMScaleY], transform[kMTransX], transform[kMTransY]};
180
181 // Actually render via Page
182 std::shared_ptr<Page> page = doc->GetPage(pageNum);
183 page->Render(bitmap, pdfiumTransform, clipLeft, clipTop, clipRight, clipBottom, renderMode,
184 showAnnotTypes, renderFormFields);
185 if (AndroidBitmap_unlockPixels(env, jbitmap) < 0) {
186 LOGE("Couldn't unlock bitmap pixel address");
187 return false;
188 }
189 return true;
190 }
191
Java_android_graphics_pdf_PdfDocumentProxy_cloneWithoutSecurity(JNIEnv * env,jobject jPdfDocument,jint destination)192 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_cloneWithoutSecurity(
193 JNIEnv* env, jobject jPdfDocument, jint destination) {
194 std::unique_lock<std::mutex> lock(mutex_);
195 LinuxFileOps::FDCloser fd(destination);
196 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
197 return doc->CloneDocumentWithoutSecurity(std::move(fd));
198 }
199
Java_android_graphics_pdf_PdfDocumentProxy_getPageText(JNIEnv * env,jobject jPdfDocument,jint pageNum)200 JNIEXPORT jstring JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageText(
201 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
202 std::unique_lock<std::mutex> lock(mutex_);
203 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
204 std::shared_ptr<Page> page = doc->GetPage(pageNum);
205
206 std::string text = page->GetTextUtf8();
207 return env->NewStringUTF(text.c_str());
208 }
209
Java_android_graphics_pdf_PdfDocumentProxy_getPageAltText(JNIEnv * env,jobject jPdfDocument,jint pageNum)210 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageAltText(
211 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
212 std::unique_lock<std::mutex> lock(mutex_);
213 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
214 std::shared_ptr<Page> page = doc->GetPage(pageNum);
215
216 vector<std::string> alt_texts;
217 page->GetAltTextUtf8(&alt_texts);
218 return convert::ToJavaStrings(env, alt_texts);
219 }
220
Java_android_graphics_pdf_PdfDocumentProxy_searchPageText(JNIEnv * env,jobject jPdfDocument,jint pageNum,jstring query)221 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_searchPageText(
222 JNIEnv* env, jobject jPdfDocument, jint pageNum, jstring query) {
223 std::unique_lock<std::mutex> lock(mutex_);
224 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
225 std::shared_ptr<Page> page = doc->GetPage(pageNum);
226 const char* query_native = env->GetStringUTFChars(query, NULL);
227
228 vector<Rectangle_i> rects;
229 vector<int> match_to_rect;
230 vector<int> char_indexes;
231 page->BoundsOfMatchesUtf8(query_native, &rects, &match_to_rect, &char_indexes);
232 jobject match_rects = convert::ToJavaMatchRects(env, rects, match_to_rect, char_indexes);
233
234 env->ReleaseStringUTFChars(query, query_native);
235 return match_rects;
236 }
237
Java_android_graphics_pdf_PdfDocumentProxy_selectPageText(JNIEnv * env,jobject jPdfDocument,jint pageNum,jobject start,jobject stop)238 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_selectPageText(
239 JNIEnv* env, jobject jPdfDocument, jint pageNum, jobject start, jobject stop) {
240 std::unique_lock<std::mutex> lock(mutex_);
241 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
242 std::shared_ptr<Page> page = doc->GetPage(pageNum);
243
244 SelectionBoundary native_start = convert::ToNativeBoundary(env, start);
245 SelectionBoundary native_stop = convert::ToNativeBoundary(env, stop);
246
247 if (native_start.index == -1 && native_stop.index == -1 &&
248 native_start.point == native_stop.point) {
249 // Starting a new selection at a point.
250 Point_i point = native_start.point;
251 if (!page->SelectWordAt(point, &native_start, &native_stop)) {
252 return NULL;
253 }
254 } else {
255 // Updating an existing selection.
256 page->ConstrainBoundary(&native_start);
257 page->ConstrainBoundary(&native_stop);
258 // Make sure start <= stop - one may have been dragged past the other.
259 if (native_start.index > native_stop.index) {
260 std::swap(native_start, native_stop);
261 }
262 }
263
264 vector<Rectangle_i> rects;
265 page->GetTextBounds(native_start.index, native_stop.index, &rects);
266 std::string text(page->GetTextUtf8(native_start.index, native_stop.index));
267 return convert::ToJavaSelection(env, pageNum, native_start, native_stop, rects, text);
268 }
269
Java_android_graphics_pdf_PdfDocumentProxy_getPageLinks(JNIEnv * env,jobject jPdfDocument,jint pageNum)270 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageLinks(
271 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
272 std::unique_lock<std::mutex> lock(mutex_);
273 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
274 std::shared_ptr<Page> page = doc->GetPage(pageNum);
275
276 vector<Rectangle_i> rects;
277 vector<int> link_to_rect;
278 vector<std::string> urls;
279 page->GetLinksUtf8(&rects, &link_to_rect, &urls);
280
281 return convert::ToJavaLinkRects(env, rects, link_to_rect, urls);
282 }
283
Java_android_graphics_pdf_PdfDocumentProxy_getPageGotoLinks(JNIEnv * env,jobject jPdfDocument,jint pageNum)284 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageGotoLinks(
285 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
286 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
287 std::shared_ptr<Page> page = doc->GetPage(pageNum);
288
289 vector<GotoLink> links = page->GetGotoLinks();
290
291 return convert::ToJavaGotoLinks(env, links);
292 }
293
Java_android_graphics_pdf_PdfDocumentProxy_retainPage(JNIEnv * env,jobject jPdfDocument,jint pageNum)294 JNIEXPORT void JNICALL Java_android_graphics_pdf_PdfDocumentProxy_retainPage(JNIEnv* env,
295 jobject jPdfDocument,
296 jint pageNum) {
297 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
298 doc->GetPage(pageNum, true);
299 }
300
Java_android_graphics_pdf_PdfDocumentProxy_releasePage(JNIEnv * env,jobject jPdfDocument,jint pageNum)301 JNIEXPORT void JNICALL Java_android_graphics_pdf_PdfDocumentProxy_releasePage(JNIEnv* env,
302 jobject jPdfDocument,
303 jint pageNum) {
304 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
305 doc->ReleaseRetainedPage(pageNum);
306 }
307
308 JNIEXPORT jboolean JNICALL
Java_android_graphics_pdf_PdfDocumentProxy_scaleForPrinting(JNIEnv * env,jobject jPdfDocument)309 Java_android_graphics_pdf_PdfDocumentProxy_scaleForPrinting(JNIEnv* env, jobject jPdfDocument) {
310 std::unique_lock<std::mutex> lock(mutex_);
311 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
312 return doc->ShouldScaleForPrinting();
313 }
314
315 JNIEXPORT jboolean JNICALL
Java_android_graphics_pdf_PdfDocumentProxy_isPdfLinearized(JNIEnv * env,jobject jPdfDocument)316 Java_android_graphics_pdf_PdfDocumentProxy_isPdfLinearized(JNIEnv* env, jobject jPdfDocument) {
317 std::unique_lock<std::mutex> lock(mutex_);
318 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
319 return doc->IsLinearized();
320 }
321
Java_android_graphics_pdf_PdfDocumentProxy_getFormType(JNIEnv * env,jobject jPdfDocument)322 JNIEXPORT jint JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getFormType(JNIEnv* env,
323 jobject jPdfDocument) {
324 std::unique_lock<std::mutex> lock(mutex_);
325 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
326 return doc->GetFormType();
327 }
328
Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfo__III(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint x,jint y)329 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfo__III(
330 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint x, jint y) {
331 std::unique_lock<std::mutex> lock(mutex_);
332 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
333 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
334
335 Point_i point{x, y};
336 FormWidgetInfo result = page->GetFormWidgetInfo(point);
337 if (!result.FoundWidget()) {
338 LOGE("No widget found at point x = %d, y = %d", x, y);
339 doc->ReleaseRetainedPage(pageNum);
340 return NULL;
341 }
342
343 doc->ReleaseRetainedPage(pageNum);
344 return convert::ToJavaFormWidgetInfo(env, result);
345 }
346
Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfo__II(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint index)347 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfo__II(
348 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint index) {
349 std::unique_lock<std::mutex> lock(mutex_);
350 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
351 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
352
353 FormWidgetInfo result = page->GetFormWidgetInfo(index);
354 if (!result.FoundWidget()) {
355 LOGE("No widget found at this index %d", index);
356 doc->ReleaseRetainedPage(pageNum);
357 return NULL;
358 }
359
360 doc->ReleaseRetainedPage(pageNum);
361 return convert::ToJavaFormWidgetInfo(env, result);
362 }
363
Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfos(JNIEnv * env,jobject jPdfDocument,jint pageNum,jintArray jTypeIds)364 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfos(
365 JNIEnv* env, jobject jPdfDocument, jint pageNum, jintArray jTypeIds) {
366 std::unique_lock<std::mutex> lock(mutex_);
367 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
368 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
369
370 std::unordered_set<int> type_ids = convert::ToNativeIntegerUnorderedSet(env, jTypeIds);
371
372 std::vector<FormWidgetInfo> widget_infos;
373 page->GetFormWidgetInfos(type_ids, &widget_infos);
374
375 doc->ReleaseRetainedPage(pageNum);
376 return convert::ToJavaFormWidgetInfos(env, widget_infos);
377 }
378
Java_android_graphics_pdf_PdfDocumentProxy_clickOnPage(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint x,jint y)379 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_clickOnPage(
380 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint x, jint y) {
381 std::unique_lock<std::mutex> lock(mutex_);
382 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
383 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
384
385 Point_i point{x, y};
386 bool clicked = page->ClickOnPoint(point);
387 if (!clicked) {
388 LOGE("Cannot click on this widget");
389 doc->ReleaseRetainedPage(pageNum);
390 return NULL;
391 }
392
393 vector<Rectangle_i> invalid_rects;
394 if (page->HasInvalidRect()) {
395 invalid_rects.push_back(page->ConsumeInvalidRect());
396 }
397 doc->ReleaseRetainedPage(pageNum);
398 return convert::ToJavaRects(env, invalid_rects);
399 }
400
Java_android_graphics_pdf_PdfDocumentProxy_setFormFieldText(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint annotationIndex,jstring jText)401 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_setFormFieldText(
402 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint annotationIndex, jstring jText) {
403 std::unique_lock<std::mutex> lock(mutex_);
404 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
405 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
406
407 const char* text = jText == nullptr ? "" : env->GetStringUTFChars(jText, nullptr);
408 bool set = page->SetFormFieldText(annotationIndex, text);
409 if (!set) {
410 LOGE("Cannot set form field text on this widget.");
411 doc->ReleaseRetainedPage(pageNum);
412 return NULL;
413 }
414
415 if (jText) {
416 env->ReleaseStringUTFChars(jText, text);
417 }
418
419 vector<Rectangle_i> invalid_rects;
420 if (page->HasInvalidRect()) {
421 invalid_rects.push_back(page->ConsumeInvalidRect());
422 }
423 doc->ReleaseRetainedPage(pageNum);
424 return convert::ToJavaRects(env, invalid_rects);
425 }
426
Java_android_graphics_pdf_PdfDocumentProxy_setFormFieldSelectedIndices(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint annotationIndex,jintArray jSelectedIndices)427 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_setFormFieldSelectedIndices(
428 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint annotationIndex,
429 jintArray jSelectedIndices) {
430 std::unique_lock<std::mutex> lock(mutex_);
431 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
432 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
433
434 vector<int> selected_indices = convert::ToNativeIntegerVector(env, jSelectedIndices);
435 bool set = page->SetChoiceSelection(annotationIndex, selected_indices);
436 if (!set) {
437 LOGE("Cannot set selected indices on this widget.");
438 doc->ReleaseRetainedPage(pageNum);
439 return NULL;
440 }
441
442 vector<Rectangle_i> invalid_rects;
443 if (page->HasInvalidRect()) {
444 invalid_rects.push_back(page->ConsumeInvalidRect());
445 }
446 doc->ReleaseRetainedPage(pageNum);
447 return convert::ToJavaRects(env, invalid_rects);
448 }
449