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