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::Annotation;
45 using pdfClient::Document;
46 using pdfClient::FileReader;
47 using pdfClient::GotoLink;
48 using pdfClient::Page;
49 using pdfClient::Point_f;
50 using pdfClient::Point_i;
51 using pdfClient::Rectangle_i;
52 using pdfClient::SelectionBoundary;
53 using pdfClient::Status;
54 using std::vector;
55
56 using pdfClient::LinuxFileOps;
57
58 namespace {
59 std::mutex mutex_;
60
61 /** Matrix organizes its values in row-major order. These constants correspond to each
62 * value in Matrix.
63 */
64 constexpr int kMScaleX = 0; // horizontal scale factor
65 constexpr int kMSkewX = 1; // horizontal skew factor
66 constexpr int kMTransX = 2; // horizontal translation
67 constexpr int kMSkewY = 3; // vertical skew factor
68 constexpr int kMScaleY = 4; // vertical scale factor
69 constexpr int kMTransY = 5; // vertical translation
70 constexpr int kMPersp0 = 6; // input x perspective factor
71 constexpr int kMPersp1 = 7; // input y perspective factor
72 constexpr int kMPersp2 = 8; // perspective bias
73 } // namespace
74
JNI_OnLoad(JavaVM * vm,void * reserved)75 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
76 std::unique_lock<std::mutex> lock(mutex_);
77 pdfClient::InitLibrary();
78 // NOTE(olsen): We never call FPDF_DestroyLibrary. Would it add any benefit?
79 return JNI_VERSION_1_6;
80 }
81
Java_android_graphics_pdf_PdfDocumentProxy_createFromFd(JNIEnv * env,jobject obj,jint jfd,jstring jpassword)82 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_createFromFd(
83 JNIEnv* env, jobject obj, jint jfd, jstring jpassword) {
84 std::unique_lock<std::mutex> lock(mutex_);
85 LinuxFileOps::FDCloser fd(jfd);
86 const char* password = jpassword == NULL ? NULL : env->GetStringUTFChars(jpassword, NULL);
87 LOGD("Creating FPDF_DOCUMENT from fd: %d", fd.get());
88 std::unique_ptr<Document> doc;
89
90 auto fileReader = std::make_unique<FileReader>(std::move(fd));
91 size_t pdfSizeInBytes = fileReader->CompleteSize();
92 Status status = Document::Load(std::move(fileReader), password,
93 /* closeFdOnFailure= */ true, &doc);
94
95 if (password) {
96 env->ReleaseStringUTFChars(jpassword, password);
97 }
98 // doc is owned by the LoadPdfResult in java.
99 return convert::ToJavaLoadPdfResult(env, status, std::move(doc), pdfSizeInBytes);
100 }
101
Java_android_graphics_pdf_PdfDocumentProxy_destroy(JNIEnv * env,jobject jPdfDocument)102 JNIEXPORT void JNICALL Java_android_graphics_pdf_PdfDocumentProxy_destroy(JNIEnv* env,
103 jobject jPdfDocument) {
104 std::unique_lock<std::mutex> lock(mutex_);
105 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
106 LOGD("Deleting Document: %p", doc);
107 delete doc;
108 LOGD("Destroyed Document: %p", doc);
109 }
110
Java_android_graphics_pdf_PdfDocumentProxy_saveToFd(JNIEnv * env,jobject jPdfDocument,jint jfd)111 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_saveToFd(JNIEnv* env,
112 jobject jPdfDocument,
113 jint jfd) {
114 std::unique_lock<std::mutex> lock(mutex_);
115 LinuxFileOps::FDCloser fd(jfd);
116 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
117 LOGD("Saving Document %p to fd %d", doc, fd.get());
118 return doc->SaveAs(std::move(fd));
119 }
120
121 // TODO(b/321979602): Cleanup Dimensions, reusing `android.util.Size`
Java_android_graphics_pdf_PdfDocumentProxy_getPageDimensions(JNIEnv * env,jobject jPdfDocument,jint pageNum)122 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageDimensions(
123 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
124 std::unique_lock<std::mutex> lock(mutex_);
125 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
126 std::shared_ptr<Page> page = doc->GetPage(pageNum);
127 Rectangle_i dimensions = page->Dimensions();
128 if (pdfClient::IsEmpty(dimensions)) {
129 LOGE("pdfClient returned 0x0 page dimensions for page %d", pageNum);
130 dimensions = pdfClient::IntRect(0, 0, 612, 792); // Default to Letter size.
131 }
132 return convert::ToJavaDimensions(env, dimensions);
133 }
134
Java_android_graphics_pdf_PdfDocumentProxy_getPageWidth(JNIEnv * env,jobject jPdfDocument,jint pageNum)135 JNIEXPORT jint JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageWidth(JNIEnv* env,
136 jobject jPdfDocument,
137 jint pageNum) {
138 std::unique_lock<std::mutex> lock(mutex_);
139 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
140 std::shared_ptr<Page> page = doc->GetPage(pageNum);
141 return page->Width();
142 }
143
Java_android_graphics_pdf_PdfDocumentProxy_getPageHeight(JNIEnv * env,jobject jPdfDocument,jint pageNum)144 JNIEXPORT jint JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageHeight(JNIEnv* env,
145 jobject jPdfDocument,
146 jint pageNum) {
147 std::unique_lock<std::mutex> lock(mutex_);
148 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
149 std::shared_ptr<Page> page = doc->GetPage(pageNum);
150 return page->Height();
151 }
152
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)153 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_render(
154 JNIEnv* env, jobject jPdfDocument, jint pageNum, jobject jbitmap, jint clipLeft,
155 jint clipTop, jint clipRight, jint clipBottom, jfloatArray jTransform, jint renderMode,
156 jint showAnnotTypes, jboolean renderFormFields) {
157 std::unique_lock<std::mutex> lock(mutex_);
158 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
159
160 // android.graphics.Bitmap -> FPDF_Bitmap
161 void* bitmap_pixels;
162 if (AndroidBitmap_lockPixels(env, jbitmap, &bitmap_pixels) < 0) {
163 LOGE("Couldn't get bitmap pixel address");
164 return false;
165 }
166 AndroidBitmapInfo info;
167 AndroidBitmap_getInfo(env, jbitmap, &info);
168 const int stride = info.width * 4;
169 FPDF_BITMAP bitmap =
170 FPDFBitmap_CreateEx(info.width, info.height, FPDFBitmap_BGRA, bitmap_pixels, stride);
171
172 // android.graphics.Matrix (SkMatrix) -> FS_Matrix
173 float transform[9];
174 env->GetFloatArrayRegion(jTransform, 0, 9, transform);
175 if (transform[kMPersp0] != 0 || transform[kMPersp1] != 0 || transform[kMPersp2] != 1) {
176 LOGE("Non-affine transform provided");
177 return false;
178 }
179
180 FS_MATRIX pdfiumTransform = {transform[kMScaleX], transform[kMSkewY], transform[kMSkewX],
181 transform[kMScaleY], transform[kMTransX], transform[kMTransY]};
182
183 // Actually render via Page
184 std::shared_ptr<Page> page = doc->GetPage(pageNum);
185 page->Render(bitmap, pdfiumTransform, clipLeft, clipTop, clipRight, clipBottom, renderMode,
186 showAnnotTypes, renderFormFields);
187 if (AndroidBitmap_unlockPixels(env, jbitmap) < 0) {
188 LOGE("Couldn't unlock bitmap pixel address");
189 return false;
190 }
191 return true;
192 }
193
Java_android_graphics_pdf_PdfDocumentProxy_cloneWithoutSecurity(JNIEnv * env,jobject jPdfDocument,jint destination)194 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_cloneWithoutSecurity(
195 JNIEnv* env, jobject jPdfDocument, jint destination) {
196 std::unique_lock<std::mutex> lock(mutex_);
197 LinuxFileOps::FDCloser fd(destination);
198 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
199 return doc->CloneDocumentWithoutSecurity(std::move(fd));
200 }
201
Java_android_graphics_pdf_PdfDocumentProxy_getPageText(JNIEnv * env,jobject jPdfDocument,jint pageNum)202 JNIEXPORT jstring JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageText(
203 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
204 std::unique_lock<std::mutex> lock(mutex_);
205 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
206 std::shared_ptr<Page> page = doc->GetPage(pageNum);
207
208 std::string text = page->GetTextUtf8();
209 return env->NewStringUTF(text.c_str());
210 }
211
Java_android_graphics_pdf_PdfDocumentProxy_getPageAltText(JNIEnv * env,jobject jPdfDocument,jint pageNum)212 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageAltText(
213 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
214 std::unique_lock<std::mutex> lock(mutex_);
215 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
216 std::shared_ptr<Page> page = doc->GetPage(pageNum);
217
218 vector<std::string> alt_texts;
219 page->GetAltTextUtf8(&alt_texts);
220 return convert::ToJavaStrings(env, alt_texts);
221 }
222
Java_android_graphics_pdf_PdfDocumentProxy_searchPageText(JNIEnv * env,jobject jPdfDocument,jint pageNum,jstring query)223 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_searchPageText(
224 JNIEnv* env, jobject jPdfDocument, jint pageNum, jstring query) {
225 std::unique_lock<std::mutex> lock(mutex_);
226 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
227 std::shared_ptr<Page> page = doc->GetPage(pageNum);
228 const char* query_native = env->GetStringUTFChars(query, NULL);
229
230 vector<Rectangle_i> rects;
231 vector<int> match_to_rect;
232 vector<int> char_indexes;
233 page->BoundsOfMatchesUtf8(query_native, &rects, &match_to_rect, &char_indexes);
234 jobject match_rects = convert::ToJavaMatchRects(env, rects, match_to_rect, char_indexes);
235
236 env->ReleaseStringUTFChars(query, query_native);
237 return match_rects;
238 }
239
Java_android_graphics_pdf_PdfDocumentProxy_selectPageText(JNIEnv * env,jobject jPdfDocument,jint pageNum,jobject start,jobject stop)240 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_selectPageText(
241 JNIEnv* env, jobject jPdfDocument, jint pageNum, jobject start, jobject stop) {
242 std::unique_lock<std::mutex> lock(mutex_);
243 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
244 std::shared_ptr<Page> page = doc->GetPage(pageNum);
245
246 SelectionBoundary native_start = convert::ToNativeBoundary(env, start);
247 SelectionBoundary native_stop = convert::ToNativeBoundary(env, stop);
248
249 if (native_start.index == -1 && native_stop.index == -1 &&
250 native_start.point == native_stop.point) {
251 // Starting a new selection at a point.
252 Point_i point = native_start.point;
253 if (!page->SelectWordAt(point, &native_start, &native_stop)) {
254 return NULL;
255 }
256 } else {
257 // Updating an existing selection.
258 page->ConstrainBoundary(&native_start);
259 page->ConstrainBoundary(&native_stop);
260 // Make sure start <= stop - one may have been dragged past the other.
261 if (native_start.index > native_stop.index) {
262 std::swap(native_start, native_stop);
263 }
264 }
265
266 vector<Rectangle_i> rects;
267 page->GetTextBounds(native_start.index, native_stop.index, &rects);
268 std::string text(page->GetTextUtf8(native_start.index, native_stop.index));
269 return convert::ToJavaSelection(env, pageNum, native_start, native_stop, rects, text);
270 }
271
Java_android_graphics_pdf_PdfDocumentProxy_getPageLinks(JNIEnv * env,jobject jPdfDocument,jint pageNum)272 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageLinks(
273 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
274 std::unique_lock<std::mutex> lock(mutex_);
275 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
276 std::shared_ptr<Page> page = doc->GetPage(pageNum);
277
278 vector<Rectangle_i> rects;
279 vector<int> link_to_rect;
280 vector<std::string> urls;
281 page->GetLinksUtf8(&rects, &link_to_rect, &urls);
282
283 return convert::ToJavaLinkRects(env, rects, link_to_rect, urls);
284 }
285
Java_android_graphics_pdf_PdfDocumentProxy_getPageGotoLinks(JNIEnv * env,jobject jPdfDocument,jint pageNum)286 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageGotoLinks(
287 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
288 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
289 std::shared_ptr<Page> page = doc->GetPage(pageNum);
290
291 vector<GotoLink> links = page->GetGotoLinks();
292
293 return convert::ToJavaGotoLinks(env, links);
294 }
295
Java_android_graphics_pdf_PdfDocumentProxy_retainPage(JNIEnv * env,jobject jPdfDocument,jint pageNum)296 JNIEXPORT void JNICALL Java_android_graphics_pdf_PdfDocumentProxy_retainPage(JNIEnv* env,
297 jobject jPdfDocument,
298 jint pageNum) {
299 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
300 doc->GetPage(pageNum, true);
301 }
302
Java_android_graphics_pdf_PdfDocumentProxy_releasePage(JNIEnv * env,jobject jPdfDocument,jint pageNum)303 JNIEXPORT void JNICALL Java_android_graphics_pdf_PdfDocumentProxy_releasePage(JNIEnv* env,
304 jobject jPdfDocument,
305 jint pageNum) {
306 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
307 doc->ReleaseRetainedPage(pageNum);
308 }
309
310 JNIEXPORT jboolean JNICALL
Java_android_graphics_pdf_PdfDocumentProxy_scaleForPrinting(JNIEnv * env,jobject jPdfDocument)311 Java_android_graphics_pdf_PdfDocumentProxy_scaleForPrinting(JNIEnv* env, jobject jPdfDocument) {
312 std::unique_lock<std::mutex> lock(mutex_);
313 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
314 return doc->ShouldScaleForPrinting();
315 }
316
317 JNIEXPORT jboolean JNICALL
Java_android_graphics_pdf_PdfDocumentProxy_isPdfLinearized(JNIEnv * env,jobject jPdfDocument)318 Java_android_graphics_pdf_PdfDocumentProxy_isPdfLinearized(JNIEnv* env, jobject jPdfDocument) {
319 std::unique_lock<std::mutex> lock(mutex_);
320 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
321 return doc->IsLinearized();
322 }
323
Java_android_graphics_pdf_PdfDocumentProxy_getFormType(JNIEnv * env,jobject jPdfDocument)324 JNIEXPORT jint JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getFormType(JNIEnv* env,
325 jobject jPdfDocument) {
326 std::unique_lock<std::mutex> lock(mutex_);
327 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
328 return doc->GetFormType();
329 }
330
Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfo__III(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint x,jint y)331 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfo__III(
332 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint x, jint y) {
333 std::unique_lock<std::mutex> lock(mutex_);
334 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
335 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
336
337 Point_i point{x, y};
338 FormWidgetInfo result = page->GetFormWidgetInfo(point);
339 if (!result.FoundWidget()) {
340 LOGE("No widget found at point x = %d, y = %d", x, y);
341 doc->ReleaseRetainedPage(pageNum);
342 return NULL;
343 }
344
345 doc->ReleaseRetainedPage(pageNum);
346 return convert::ToJavaFormWidgetInfo(env, result);
347 }
348
Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfo__II(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint index)349 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfo__II(
350 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint index) {
351 std::unique_lock<std::mutex> lock(mutex_);
352 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
353 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
354
355 FormWidgetInfo result = page->GetFormWidgetInfo(index);
356 if (!result.FoundWidget()) {
357 LOGE("No widget found at this index %d", index);
358 doc->ReleaseRetainedPage(pageNum);
359 return NULL;
360 }
361
362 doc->ReleaseRetainedPage(pageNum);
363 return convert::ToJavaFormWidgetInfo(env, result);
364 }
365
Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfos(JNIEnv * env,jobject jPdfDocument,jint pageNum,jintArray jTypeIds)366 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getFormWidgetInfos(
367 JNIEnv* env, jobject jPdfDocument, jint pageNum, jintArray jTypeIds) {
368 std::unique_lock<std::mutex> lock(mutex_);
369 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
370 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
371
372 std::unordered_set<int> type_ids = convert::ToNativeIntegerUnorderedSet(env, jTypeIds);
373
374 std::vector<FormWidgetInfo> widget_infos;
375 page->GetFormWidgetInfos(type_ids, &widget_infos);
376
377 doc->ReleaseRetainedPage(pageNum);
378 return convert::ToJavaFormWidgetInfos(env, widget_infos);
379 }
380
Java_android_graphics_pdf_PdfDocumentProxy_clickOnPage(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint x,jint y)381 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_clickOnPage(
382 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint x, jint y) {
383 std::unique_lock<std::mutex> lock(mutex_);
384 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
385 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
386
387 Point_i point{x, y};
388 bool clicked = page->ClickOnPoint(point);
389 if (!clicked) {
390 LOGE("Cannot click on this widget");
391 doc->ReleaseRetainedPage(pageNum);
392 return NULL;
393 }
394
395 vector<Rectangle_i> invalid_rects;
396 if (page->HasInvalidRect()) {
397 invalid_rects.push_back(page->ConsumeInvalidRect());
398 }
399 doc->ReleaseRetainedPage(pageNum);
400 return convert::ToJavaRects(env, invalid_rects);
401 }
402
Java_android_graphics_pdf_PdfDocumentProxy_setFormFieldText(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint annotationIndex,jstring jText)403 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_setFormFieldText(
404 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint annotationIndex, jstring jText) {
405 std::unique_lock<std::mutex> lock(mutex_);
406 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
407 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
408
409 const char* text = jText == nullptr ? "" : env->GetStringUTFChars(jText, nullptr);
410 bool set = page->SetFormFieldText(annotationIndex, text);
411 if (!set) {
412 LOGE("Cannot set form field text on this widget.");
413 doc->ReleaseRetainedPage(pageNum);
414 return NULL;
415 }
416
417 if (jText) {
418 env->ReleaseStringUTFChars(jText, text);
419 }
420
421 vector<Rectangle_i> invalid_rects;
422 if (page->HasInvalidRect()) {
423 invalid_rects.push_back(page->ConsumeInvalidRect());
424 }
425 doc->ReleaseRetainedPage(pageNum);
426 return convert::ToJavaRects(env, invalid_rects);
427 }
428
Java_android_graphics_pdf_PdfDocumentProxy_setFormFieldSelectedIndices(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint annotationIndex,jintArray jSelectedIndices)429 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_setFormFieldSelectedIndices(
430 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint annotationIndex,
431 jintArray jSelectedIndices) {
432 std::unique_lock<std::mutex> lock(mutex_);
433 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
434 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
435
436 vector<int> selected_indices = convert::ToNativeIntegerVector(env, jSelectedIndices);
437 bool set = page->SetChoiceSelection(annotationIndex, selected_indices);
438 if (!set) {
439 LOGE("Cannot set selected indices on this widget.");
440 doc->ReleaseRetainedPage(pageNum);
441 return NULL;
442 }
443
444 vector<Rectangle_i> invalid_rects;
445 if (page->HasInvalidRect()) {
446 invalid_rects.push_back(page->ConsumeInvalidRect());
447 }
448 doc->ReleaseRetainedPage(pageNum);
449 return convert::ToJavaRects(env, invalid_rects);
450 }
451
Java_android_graphics_pdf_PdfDocumentProxy_addPageObject(JNIEnv * env,jobject jPdfDocument,jint pageNum,jobject jPageObject)452 JNIEXPORT jint JNICALL Java_android_graphics_pdf_PdfDocumentProxy_addPageObject(
453 JNIEnv* env, jobject jPdfDocument, jint pageNum, jobject jPageObject) {
454 std::unique_lock<std::mutex> lock(mutex_);
455 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
456 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
457
458 std::unique_ptr<PageObject> page_object =
459 convert::ToNativePageObject(env, jPageObject, page.get());
460
461 if (!page_object) {
462 return -1;
463 }
464
465 int new_object_index = page->AddPageObject(std::move(page_object));
466
467 doc->ReleaseRetainedPage(pageNum);
468 return new_object_index;
469 }
470
Java_android_graphics_pdf_PdfDocumentProxy_getPageObjects(JNIEnv * env,jobject jPdfDocument,jint pageNum)471 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageObjects(
472 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
473 std::unique_lock<std::mutex> lock(mutex_);
474 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
475 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
476
477 std::vector<PageObject*> page_objects = page->GetPageObjects();
478
479 doc->ReleaseRetainedPage(pageNum);
480 return convert::ToJavaPdfPageObjects(env, page_objects, page.get());
481 }
482
Java_android_graphics_pdf_PdfDocumentProxy_removePageObject(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint index)483 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_removePageObject(
484 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint index) {
485 std::unique_lock<std::mutex> lock(mutex_);
486 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
487 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
488
489 bool removed = page->RemovePageObject(index);
490
491 doc->ReleaseRetainedPage(pageNum);
492 return removed;
493 }
494
Java_android_graphics_pdf_PdfDocumentProxy_updatePageObject(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint index,jobject jPageObject)495 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_updatePageObject(
496 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint index, jobject jPageObject) {
497 std::unique_lock<std::mutex> lock(mutex_);
498 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
499 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
500
501 std::unique_ptr<PageObject> page_object =
502 convert::ToNativePageObject(env, jPageObject, page.get());
503
504 if (!page_object) {
505 return false;
506 }
507
508 bool updated = page->UpdatePageObject(index, std::move(page_object));
509
510 doc->ReleaseRetainedPage(pageNum);
511 return updated;
512 }
513
Java_android_graphics_pdf_PdfDocumentProxy_getPageAnnotations(JNIEnv * env,jobject jPdfDocument,jint pageNum)514 JNIEXPORT jobject JNICALL Java_android_graphics_pdf_PdfDocumentProxy_getPageAnnotations(
515 JNIEnv* env, jobject jPdfDocument, jint pageNum) {
516 std::unique_lock<std::mutex> lock(mutex_);
517 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
518 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
519
520 std::vector<Annotation*> annotations = page->GetPageAnnotations();
521
522 doc->ReleaseRetainedPage(pageNum);
523 return convert::ToJavaPageAnnotations(env, annotations, page.get());
524 }
525
Java_android_graphics_pdf_PdfDocumentProxy_addPageAnnotation(JNIEnv * env,jobject jPdfDocument,jint pageNum,jobject jPageAnnotation)526 JNIEXPORT jint JNICALL Java_android_graphics_pdf_PdfDocumentProxy_addPageAnnotation(
527 JNIEnv* env, jobject jPdfDocument, jint pageNum, jobject jPageAnnotation) {
528 std::unique_lock<std::mutex> lock(mutex_);
529 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
530 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
531
532 std::unique_ptr<Annotation> annotation =
533 convert::ToNativePageAnnotation(env, jPageAnnotation, page.get());
534
535 if (!annotation) {
536 return -1;
537 }
538
539 int new_annotation_index = page->AddPageAnnotation(std::move(annotation));
540
541 doc->ReleaseRetainedPage(pageNum);
542 return new_annotation_index;
543 }
544
Java_android_graphics_pdf_PdfDocumentProxy_removePageAnnotation(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint index)545 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_removePageAnnotation(
546 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint index) {
547 std::unique_lock<std::mutex> lock(mutex_);
548 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
549 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
550
551 bool removed = page->RemovePageAnnotation(index);
552
553 doc->ReleaseRetainedPage(pageNum);
554 return removed;
555 }
556
Java_android_graphics_pdf_PdfDocumentProxy_updatePageAnnotation(JNIEnv * env,jobject jPdfDocument,jint pageNum,jint index,jobject jPageAnnotation)557 JNIEXPORT jboolean JNICALL Java_android_graphics_pdf_PdfDocumentProxy_updatePageAnnotation(
558 JNIEnv* env, jobject jPdfDocument, jint pageNum, jint index, jobject jPageAnnotation) {
559 std::unique_lock<std::mutex> lock(mutex_);
560 Document* doc = convert::GetPdfDocPtr(env, jPdfDocument);
561 std::shared_ptr<Page> page = doc->GetPage(pageNum, true);
562
563 std::unique_ptr<Annotation> annotation =
564 convert::ToNativePageAnnotation(env, jPageAnnotation, page.get());
565
566 if (!annotation) {
567 return false;
568 }
569
570 bool updated = page->UpdatePageAnnotation(index, std::move(annotation));
571
572 doc->ReleaseRetainedPage(pageNum);
573 return updated;
574 }
575