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 "GraphicsJNI.h"
     20 #include "SkBitmap.h"
     21 #include "SkMatrix.h"
     22 #include "fpdfview.h"
     23 #include "fsdk_rendercontext.h"
     24 
     25 #include <android_runtime/AndroidRuntime.h>
     26 #include <vector>
     27 #include <utils/Log.h>
     28 #include <unistd.h>
     29 #include <sys/types.h>
     30 #include <unistd.h>
     31 
     32 namespace android {
     33 
     34 static const int RENDER_MODE_FOR_DISPLAY = 1;
     35 static const int RENDER_MODE_FOR_PRINT = 2;
     36 
     37 static struct {
     38     jfieldID x;
     39     jfieldID y;
     40 } gPointClassInfo;
     41 
     42 static Mutex sLock;
     43 
     44 static int sUnmatchedInitRequestCount = 0;
     45 
     46 static void initializeLibraryIfNeeded() {
     47     Mutex::Autolock _l(sLock);
     48     if (sUnmatchedInitRequestCount == 0) {
     49         FPDF_InitLibrary(NULL);
     50     }
     51     sUnmatchedInitRequestCount++;
     52 }
     53 
     54 static void destroyLibraryIfNeeded() {
     55     Mutex::Autolock _l(sLock);
     56     sUnmatchedInitRequestCount--;
     57     if (sUnmatchedInitRequestCount == 0) {
     58        FPDF_DestroyLibrary();
     59     }
     60 }
     61 
     62 static int getBlock(void* param, unsigned long position, unsigned char* outBuffer,
     63         unsigned long size) {
     64     const int fd = reinterpret_cast<intptr_t>(param);
     65     const int readCount = pread(fd, outBuffer, size, position);
     66     if (readCount < 0) {
     67         ALOGE("Cannot read from file descriptor. Error:%d", errno);
     68         return 0;
     69     }
     70     return 1;
     71 }
     72 
     73 static jlong nativeCreate(JNIEnv* env, jclass thiz, jint fd, jlong size) {
     74     initializeLibraryIfNeeded();
     75 
     76     FPDF_FILEACCESS loader;
     77     loader.m_FileLen = size;
     78     loader.m_Param = reinterpret_cast<void*>(intptr_t(fd));
     79     loader.m_GetBlock = &getBlock;
     80 
     81     FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL);
     82 
     83     if (!document) {
     84         const long error = FPDF_GetLastError();
     85         switch (error) {
     86             case FPDF_ERR_PASSWORD:
     87             case FPDF_ERR_SECURITY: {
     88                 jniThrowException(env, "java/lang/SecurityException",
     89                         "cannot create document. Error:" + error);
     90             } break;
     91             default: {
     92                 jniThrowException(env, "java/io/IOException",
     93                         "cannot create document. Error:" + error);
     94             } break;
     95         }
     96         destroyLibraryIfNeeded();
     97         return -1;
     98     }
     99 
    100     return reinterpret_cast<jlong>(document);
    101 }
    102 
    103 static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr,
    104         jint pageIndex, jobject outSize) {
    105     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    106 
    107     FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
    108 
    109     if (!page) {
    110         jniThrowException(env, "java/lang/IllegalStateException",
    111                 "cannot load page");
    112         return -1;
    113     }
    114 
    115     double width = 0;
    116     double height = 0;
    117 
    118     const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height);
    119 
    120     if (!result) {
    121         jniThrowException(env, "java/lang/IllegalStateException",
    122                     "cannot get page size");
    123         return -1;
    124     }
    125 
    126     env->SetIntField(outSize, gPointClassInfo.x, width);
    127     env->SetIntField(outSize, gPointClassInfo.y, height);
    128 
    129     return reinterpret_cast<jlong>(page);
    130 }
    131 
    132 static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) {
    133     FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr);
    134     FPDF_ClosePage(page);
    135 }
    136 
    137 static void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) {
    138     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    139     FPDF_CloseDocument(document);
    140     destroyLibraryIfNeeded();
    141 }
    142 
    143 static jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) {
    144     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    145     return FPDF_GetPageCount(document);
    146 }
    147 
    148 static jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) {
    149     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    150     return FPDF_VIEWERREF_GetPrintScaling(document);
    151 }
    152 
    153 static void DropContext(void* data) {
    154     delete (CRenderContext*) data;
    155 }
    156 
    157 static void renderPageBitmap(FPDF_BITMAP bitmap, FPDF_PAGE page, int destLeft, int destTop,
    158         int destRight, int destBottom, SkMatrix* transform, int flags) {
    159     // Note: this code ignores the currently unused RENDER_NO_NATIVETEXT,
    160     // FPDF_RENDER_LIMITEDIMAGECACHE, FPDF_RENDER_FORCEHALFTONE, FPDF_GRAYSCALE,
    161     // and FPDF_ANNOT flags. To add support for that refer to FPDF_RenderPage_Retail
    162     // in fpdfview.cpp
    163 
    164     CRenderContext* pContext = FX_NEW CRenderContext;
    165 
    166     CPDF_Page* pPage = (CPDF_Page*) page;
    167     pPage->SetPrivateData((void*) 1, pContext, DropContext);
    168 
    169     CFX_FxgeDevice* fxgeDevice = FX_NEW CFX_FxgeDevice;
    170     pContext->m_pDevice = fxgeDevice;
    171 
    172     // Reverse the bytes (last argument TRUE) since the Android
    173     // format is ARGB while the renderer uses BGRA internally.
    174     fxgeDevice->Attach((CFX_DIBitmap*) bitmap, 0, TRUE);
    175 
    176     CPDF_RenderOptions* renderOptions = pContext->m_pOptions;
    177 
    178     if (!renderOptions) {
    179         renderOptions = FX_NEW CPDF_RenderOptions;
    180         pContext->m_pOptions = renderOptions;
    181     }
    182 
    183     if (flags & FPDF_LCD_TEXT) {
    184         renderOptions->m_Flags |= RENDER_CLEARTYPE;
    185     } else {
    186         renderOptions->m_Flags &= ~RENDER_CLEARTYPE;
    187     }
    188 
    189     const CPDF_OCContext::UsageType usage = (flags & FPDF_PRINTING)
    190             ? CPDF_OCContext::Print : CPDF_OCContext::View;
    191 
    192     renderOptions->m_AddFlags = flags >> 8;
    193     renderOptions->m_pOCContext = new CPDF_OCContext(pPage->m_pDocument, usage);
    194 
    195     fxgeDevice->SaveState();
    196 
    197     FX_RECT clip;
    198     clip.left = destLeft;
    199     clip.right = destRight;
    200     clip.top = destTop;
    201     clip.bottom = destBottom;
    202     fxgeDevice->SetClip_Rect(&clip);
    203 
    204     CPDF_RenderContext* pageContext = FX_NEW CPDF_RenderContext;
    205     pContext->m_pContext = pageContext;
    206     pageContext->Create(pPage);
    207 
    208     CFX_AffineMatrix matrix;
    209     if (!transform) {
    210         pPage->GetDisplayMatrix(matrix, destLeft, destTop, destRight - destLeft,
    211                 destBottom - destTop, 0);
    212     } else {
    213         // PDF's coordinate system origin is left-bottom while
    214         // in graphics it is the top-left, so remap the origin.
    215         matrix.Set(1, 0, 0, -1, 0, pPage->GetPageHeight());
    216 
    217         SkScalar transformValues[6];
    218         transform->asAffine(transformValues);
    219 
    220         matrix.Concat(transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY],
    221                 transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY],
    222                 transformValues[SkMatrix::kATransX], transformValues[SkMatrix::kATransY]);
    223     }
    224     pageContext->AppendObjectList(pPage, &matrix);
    225 
    226     pContext->m_pRenderer = FX_NEW CPDF_ProgressiveRenderer;
    227     pContext->m_pRenderer->Start(pageContext, fxgeDevice, renderOptions, NULL);
    228 
    229     fxgeDevice->RestoreState();
    230 
    231     pPage->RemovePrivateData((void*) 1);
    232 
    233     delete pContext;
    234 }
    235 
    236 static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr,
    237         jlong bitmapPtr, jint destLeft, jint destTop, jint destRight, jint destBottom,
    238         jlong matrixPtr, jint renderMode) {
    239 
    240     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    241     FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr);
    242     SkBitmap* skBitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    243     SkMatrix* skMatrix = reinterpret_cast<SkMatrix*>(matrixPtr);
    244 
    245     skBitmap->lockPixels();
    246 
    247     const int stride = skBitmap->width() * 4;
    248 
    249     FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap->width(), skBitmap->height(),
    250             FPDFBitmap_BGRA, skBitmap->getPixels(), stride);
    251 
    252     if (!bitmap) {
    253         ALOGE("Erorr creating bitmap");
    254         return;
    255     }
    256 
    257     int renderFlags = 0;
    258     if (renderMode == RENDER_MODE_FOR_DISPLAY) {
    259         renderFlags |= FPDF_LCD_TEXT;
    260     } else if (renderMode == RENDER_MODE_FOR_PRINT) {
    261         renderFlags |= FPDF_PRINTING;
    262     }
    263 
    264     renderPageBitmap(bitmap, page, destLeft, destTop, destRight,
    265             destBottom, skMatrix, renderFlags);
    266 
    267     skBitmap->notifyPixelsChanged();
    268     skBitmap->unlockPixels();
    269 }
    270 
    271 static JNINativeMethod gPdfRenderer_Methods[] = {
    272     {"nativeCreate", "(IJ)J", (void*) nativeCreate},
    273     {"nativeClose", "(J)V", (void*) nativeClose},
    274     {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
    275     {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting},
    276     {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage},
    277     {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize},
    278     {"nativeClosePage", "(J)V", (void*) nativeClosePage}
    279 };
    280 
    281 int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) {
    282     int result = android::AndroidRuntime::registerNativeMethods(
    283             env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods,
    284             NELEM(gPdfRenderer_Methods));
    285 
    286     jclass clazz = env->FindClass("android/graphics/Point");
    287     gPointClassInfo.x = env->GetFieldID(clazz, "x", "I");
    288     gPointClassInfo.y = env->GetFieldID(clazz, "y", "I");
    289 
    290     return result;
    291 };
    292 
    293 };
    294