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 "JNIHelp.h"
21
22#include "fpdfview.h"
23
24#define LOG_TAG "PdfUtils"
25#include <utils/Log.h>
26
27namespace android {
28
29static int sUnmatchedPdfiumInitRequestCount = 0;
30
31int 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.
44bool 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
81static bool initializeLibraryIfNeeded(JNIEnv* env) {
82    if (sUnmatchedPdfiumInitRequestCount == 0) {
83        FPDF_InitLibrary();
84
85        HANDLE_PDFIUM_ERROR_STATE_WITH_RET_CODE(env, false);
86    }
87
88    sUnmatchedPdfiumInitRequestCount++;
89    return true;
90}
91
92static void destroyLibraryIfNeeded(JNIEnv* env, bool handleError) {
93    if (sUnmatchedPdfiumInitRequestCount == 1) {
94       FPDF_DestroyLibrary();
95
96       if (handleError) {
97           HANDLE_PDFIUM_ERROR_STATE(env);
98       }
99    }
100
101    sUnmatchedPdfiumInitRequestCount--;
102}
103
104jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size) {
105    bool isInitialized = initializeLibraryIfNeeded(env);
106    if (!isInitialized) {
107        return -1;
108    }
109
110    FPDF_FILEACCESS loader;
111    loader.m_FileLen = size;
112    loader.m_Param = reinterpret_cast<void*>(intptr_t(fd));
113    loader.m_GetBlock = &getBlock;
114
115    FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL);
116    if (!document) {
117        forwardPdfiumError(env);
118        destroyLibraryIfNeeded(env, false);
119        return -1;
120    }
121
122    return reinterpret_cast<jlong>(document);
123}
124
125void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) {
126    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
127    FPDF_CloseDocument(document);
128    HANDLE_PDFIUM_ERROR_STATE(env)
129
130    destroyLibraryIfNeeded(env, true);
131}
132
133jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) {
134    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
135
136    int pageCount = FPDF_GetPageCount(document);
137    HANDLE_PDFIUM_ERROR_STATE_WITH_RET_CODE(env, -1);
138
139    return pageCount;
140}
141
142jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) {
143    FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
144
145    FPDF_BOOL printScaling = FPDF_VIEWERREF_GetPrintScaling(document);
146    HANDLE_PDFIUM_ERROR_STATE_WITH_RET_CODE(env, false);
147
148    return printScaling ? JNI_TRUE : JNI_FALSE;
149}
150
151};
152