1 /*
2  * Copyright (C) 2016 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 "PdfUtils.h"
18 
19 #include "jni.h"
20 #include <nativehelper/JNIHelp.h>
21 
22 #include "fpdfview.h"
23 
24 #define LOG_TAG "PdfUtils"
25 #include <utils/Log.h>
26 
27 namespace android {
28 
29 static int sUnmatchedPdfiumInitRequestCount = 0;
30 
getBlock(void * param,unsigned long position,unsigned char * outBuffer,unsigned long size)31 int getBlock(void* param, unsigned long position, unsigned char* outBuffer,
32         unsigned long size) {
33     const int fd = reinterpret_cast<intptr_t>(param);
34     const int readCount = pread(fd, outBuffer, size, position);
35     if (readCount < 0) {
36         ALOGE("Cannot read from file descriptor. Error:%d", errno);
37         return 0;
38     }
39     return 1;
40 }
41 
42 // Check if the last pdfium command failed and if so, forward the error to java via an exception. If
43 // this function returns true an exception is pending.
forwardPdfiumError(JNIEnv * env)44 bool forwardPdfiumError(JNIEnv* env) {
45     long error = FPDF_GetLastError();
46     switch (error) {
47         case FPDF_ERR_SUCCESS:
48             return false;
49         case FPDF_ERR_FILE:
50             jniThrowException(env, "java/io/IOException", "file not found or cannot be opened");
51             break;
52         case FPDF_ERR_FORMAT:
53             jniThrowException(env, "java/io/IOException", "file not in PDF format or corrupted");
54             break;
55         case FPDF_ERR_PASSWORD:
56             jniThrowException(env, "java/lang/SecurityException",
57                     "password required or incorrect password");
58             break;
59         case FPDF_ERR_SECURITY:
60             jniThrowException(env, "java/lang/SecurityException", "unsupported security scheme");
61             break;
62         case FPDF_ERR_PAGE:
63             jniThrowException(env, "java/io/IOException", "page not found or content error");
64             break;
65 #ifdef PDF_ENABLE_XFA
66         case FPDF_ERR_XFALOAD:
67             jniThrowException(env, "java/lang/Exception", "load XFA error");
68             break;
69         case FPDF_ERR_XFALAYOUT:
70             jniThrowException(env, "java/lang/Exception", "layout XFA error");
71             break;
72 #endif  // PDF_ENABLE_XFA
73         case FPDF_ERR_UNKNOWN:
74         default:
75             jniThrowExceptionFmt(env, "java/lang/Exception", "unknown error %d", error);
76     }
77 
78     return true;
79 }
80 
initializeLibraryIfNeeded(JNIEnv * env)81 static void initializeLibraryIfNeeded(JNIEnv* env) {
82     if (sUnmatchedPdfiumInitRequestCount == 0) {
83         FPDF_InitLibrary();
84     }
85 
86     sUnmatchedPdfiumInitRequestCount++;
87 }
88 
destroyLibraryIfNeeded(JNIEnv * env,bool handleError)89 static void destroyLibraryIfNeeded(JNIEnv* env, bool handleError) {
90     if (sUnmatchedPdfiumInitRequestCount == 1) {
91         FPDF_DestroyLibrary();
92     }
93 
94     sUnmatchedPdfiumInitRequestCount--;
95 }
96 
nativeOpen(JNIEnv * env,jclass thiz,jint fd,jlong size)97 jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size) {
98     initializeLibraryIfNeeded(env);
99 
100     FPDF_FILEACCESS loader;
101     loader.m_FileLen = size;
102     loader.m_Param = reinterpret_cast<void*>(intptr_t(fd));
103     loader.m_GetBlock = &getBlock;
104 
105     FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL);
106     if (!document) {
107         forwardPdfiumError(env);
108         destroyLibraryIfNeeded(env, false);
109         return -1;
110     }
111 
112     return reinterpret_cast<jlong>(document);
113 }
114 
nativeClose(JNIEnv * env,jclass thiz,jlong documentPtr)115 void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) {
116     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
117     FPDF_CloseDocument(document);
118 
119     destroyLibraryIfNeeded(env, true);
120 }
121 
nativeGetPageCount(JNIEnv * env,jclass thiz,jlong documentPtr)122 jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) {
123     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
124 
125     return FPDF_GetPageCount(document);
126 }
127 
nativeScaleForPrinting(JNIEnv * env,jclass thiz,jlong documentPtr)128 jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) {
129     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
130     FPDF_BOOL printScaling = FPDF_VIEWERREF_GetPrintScaling(document);
131 
132     return printScaling ? JNI_TRUE : JNI_FALSE;
133 }
134 
135 };
136