Home | History | Annotate | Download | only in pdf
      1 /*
      2  * Copyright (C) 2014 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 "jni.h"
     18 #include "JNIHelp.h"
     19 #include "fpdfview.h"
     20 #include "fpdfedit.h"
     21 #include "fpdfsave.h"
     22 
     23 #include <android_runtime/AndroidRuntime.h>
     24 #include <vector>
     25 #include <utils/Log.h>
     26 #include <unistd.h>
     27 #include <sys/types.h>
     28 #include <unistd.h>
     29 
     30 namespace android {
     31 
     32 static Mutex sLock;
     33 
     34 static int sUnmatchedInitRequestCount = 0;
     35 
     36 static void initializeLibraryIfNeeded() {
     37     Mutex::Autolock _l(sLock);
     38     if (sUnmatchedInitRequestCount == 0) {
     39         FPDF_InitLibrary(NULL);
     40     }
     41     sUnmatchedInitRequestCount++;
     42 }
     43 
     44 static void destroyLibraryIfNeeded() {
     45     Mutex::Autolock _l(sLock);
     46     sUnmatchedInitRequestCount--;
     47     if (sUnmatchedInitRequestCount == 0) {
     48        FPDF_DestroyLibrary();
     49     }
     50 }
     51 
     52 static int getBlock(void* param, unsigned long position, unsigned char* outBuffer,
     53         unsigned long size) {
     54     const int fd = reinterpret_cast<intptr_t>(param);
     55     const int readCount = pread(fd, outBuffer, size, position);
     56     if (readCount < 0) {
     57         ALOGE("Cannot read from file descriptor. Error:%d", errno);
     58         return 0;
     59     }
     60     return 1;
     61 }
     62 
     63 static jlong nativeOpen(JNIEnv* env, jclass thiz, jint fd, jlong size) {
     64     initializeLibraryIfNeeded();
     65 
     66     FPDF_FILEACCESS loader;
     67     loader.m_FileLen = size;
     68     loader.m_Param = reinterpret_cast<void*>(intptr_t(fd));
     69     loader.m_GetBlock = &getBlock;
     70 
     71     FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL);
     72 
     73     if (!document) {
     74         const long error = FPDF_GetLastError();
     75         jniThrowException(env, "java/io/IOException",
     76                 "cannot create document. Error:" + error);
     77         destroyLibraryIfNeeded();
     78         return -1;
     79     }
     80 
     81     return reinterpret_cast<jlong>(document);
     82 }
     83 
     84 static void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) {
     85     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
     86     FPDF_CloseDocument(document);
     87     destroyLibraryIfNeeded();
     88 }
     89 
     90 static jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) {
     91     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
     92     return FPDF_GetPageCount(document);
     93 }
     94 
     95 static jint nativeRemovePage(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex) {
     96     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
     97     FPDFPage_Delete(document, pageIndex);
     98     return FPDF_GetPageCount(document);
     99 }
    100 
    101 struct PdfToFdWriter : FPDF_FILEWRITE {
    102     int dstFd;
    103 };
    104 
    105 static bool writeAllBytes(const int fd, const void* buffer, const size_t byteCount) {
    106     char* writeBuffer = static_cast<char*>(const_cast<void*>(buffer));
    107     size_t remainingBytes = byteCount;
    108     while (remainingBytes > 0) {
    109         ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes);
    110         if (writtenByteCount == -1) {
    111             if (errno == EINTR) {
    112                 continue;
    113             }
    114             __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
    115                     "Error writing to buffer: %d", errno);
    116             return false;
    117         }
    118         remainingBytes -= writtenByteCount;
    119         writeBuffer += writtenByteCount;
    120     }
    121     return true;
    122 }
    123 
    124 static int writeBlock(FPDF_FILEWRITE* owner, const void* buffer, unsigned long size) {
    125     const PdfToFdWriter* writer = reinterpret_cast<PdfToFdWriter*>(owner);
    126     const bool success = writeAllBytes(writer->dstFd, buffer, size);
    127     if (success < 0) {
    128         ALOGE("Cannot write to file descriptor. Error:%d", errno);
    129         return 0;
    130     }
    131     return 1;
    132 }
    133 
    134 static void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) {
    135     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    136     PdfToFdWriter writer;
    137     writer.dstFd = fd;
    138     writer.WriteBlock = &writeBlock;
    139     const bool success = FPDF_SaveAsCopy(document, &writer, FPDF_NO_INCREMENTAL);
    140     if (!success) {
    141         jniThrowException(env, "java/io/IOException",
    142                 "cannot write to fd. Error:" + errno);
    143         destroyLibraryIfNeeded();
    144     }
    145 }
    146 
    147 static JNINativeMethod gPdfEditor_Methods[] = {
    148     {"nativeOpen", "(IJ)J", (void*) nativeOpen},
    149     {"nativeClose", "(J)V", (void*) nativeClose},
    150     {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
    151     {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage},
    152     {"nativeWrite", "(JI)V", (void*) nativeWrite}
    153 };
    154 
    155 int register_android_graphics_pdf_PdfEditor(JNIEnv* env) {
    156     return android::AndroidRuntime::registerNativeMethods(
    157             env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods,
    158             NELEM(gPdfEditor_Methods));
    159 };
    160 
    161 };
    162