• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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