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         jniThrowException(env, "java/io/IOException",
     86                 "cannot create document. Error:" + error);
     87         destroyLibraryIfNeeded();
     88         return -1;
     89     }
     90 
     91     return reinterpret_cast<jlong>(document);
     92 }
     93 
     94 static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr,
     95         jint pageIndex, jobject outSize) {
     96     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
     97 
     98     FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
     99 
    100     if (!page) {
    101         jniThrowException(env, "java/lang/IllegalStateException",
    102                 "cannot load page");
    103         return -1;
    104     }
    105 
    106     double width = 0;
    107     double height = 0;
    108 
    109     const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height);
    110 
    111     if (!result) {
    112         jniThrowException(env, "java/lang/IllegalStateException",
    113                     "cannot get page size");
    114         return -1;
    115     }
    116 
    117     env->SetIntField(outSize, gPointClassInfo.x, width);
    118     env->SetIntField(outSize, gPointClassInfo.y, height);
    119 
    120     return reinterpret_cast<jlong>(page);
    121 }
    122 
    123 static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) {
    124     FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr);
    125     FPDF_ClosePage(page);
    126 }
    127 
    128 static void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) {
    129     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    130     FPDF_CloseDocument(document);
    131     destroyLibraryIfNeeded();
    132 }
    133 
    134 static jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) {
    135     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    136     return FPDF_GetPageCount(document);
    137 }
    138 
    139 static jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) {
    140     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    141     return FPDF_VIEWERREF_GetPrintScaling(document);
    142 }
    143 
    144 static void DropContext(void* data) {
    145     delete (CRenderContext*) data;
    146 }
    147 
    148 static void renderPageBitmap(FPDF_BITMAP bitmap, FPDF_PAGE page, int destLeft, int destTop,
    149         int destRight, int destBottom, SkMatrix* transform, int flags) {
    150     // Note: this code ignores the currently unused RENDER_NO_NATIVETEXT,
    151     // FPDF_RENDER_LIMITEDIMAGECACHE, FPDF_RENDER_FORCEHALFTONE, FPDF_GRAYSCALE,
    152     // and FPDF_ANNOT flags. To add support for that refer to FPDF_RenderPage_Retail
    153     // in fpdfview.cpp
    154 
    155     CRenderContext* pContext = FX_NEW CRenderContext;
    156 
    157     CPDF_Page* pPage = (CPDF_Page*) page;
    158     pPage->SetPrivateData((void*) 1, pContext, DropContext);
    159 
    160     CFX_FxgeDevice* fxgeDevice = FX_NEW CFX_FxgeDevice;
    161     pContext->m_pDevice = fxgeDevice;
    162 
    163     // Reverse the bytes (last argument TRUE) since the Android
    164     // format is ARGB while the renderer uses BGRA internally.
    165     fxgeDevice->Attach((CFX_DIBitmap*) bitmap, 0, TRUE);
    166 
    167     CPDF_RenderOptions* renderOptions = pContext->m_pOptions;
    168 
    169     if (!renderOptions) {
    170         renderOptions = FX_NEW CPDF_RenderOptions;
    171         pContext->m_pOptions = renderOptions;
    172     }
    173 
    174     if (flags & FPDF_LCD_TEXT) {
    175         renderOptions->m_Flags |= RENDER_CLEARTYPE;
    176     } else {
    177         renderOptions->m_Flags &= ~RENDER_CLEARTYPE;
    178     }
    179 
    180     const CPDF_OCContext::UsageType usage = (flags & FPDF_PRINTING)
    181             ? CPDF_OCContext::Print : CPDF_OCContext::View;
    182 
    183     renderOptions->m_AddFlags = flags >> 8;
    184     renderOptions->m_pOCContext = new CPDF_OCContext(pPage->m_pDocument, usage);
    185 
    186     fxgeDevice->SaveState();
    187 
    188     FX_RECT clip;
    189     clip.left = destLeft;
    190     clip.right = destRight;
    191     clip.top = destTop;
    192     clip.bottom = destBottom;
    193     fxgeDevice->SetClip_Rect(&clip);
    194 
    195     CPDF_RenderContext* pageContext = FX_NEW CPDF_RenderContext;
    196     pContext->m_pContext = pageContext;
    197     pageContext->Create(pPage);
    198 
    199     CFX_AffineMatrix matrix;
    200     if (!transform) {
    201         pPage->GetDisplayMatrix(matrix, destLeft, destTop, destRight - destLeft,
    202                 destBottom - destTop, 0);
    203     } else {
    204         // PDF's coordinate system origin is left-bottom while
    205         // in graphics it is the top-left, so remap the origin.
    206         matrix.Set(1, 0, 0, -1, 0, pPage->GetPageHeight());
    207 
    208         SkScalar transformValues[6];
    209         transform->asAffine(transformValues);
    210 
    211         matrix.Concat(transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY],
    212                 transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY],
    213                 transformValues[SkMatrix::kATransX], transformValues[SkMatrix::kATransY]);
    214     }
    215     pageContext->AppendObjectList(pPage, &matrix);
    216 
    217     pContext->m_pRenderer = FX_NEW CPDF_ProgressiveRenderer;
    218     pContext->m_pRenderer->Start(pageContext, fxgeDevice, renderOptions, NULL);
    219 
    220     fxgeDevice->RestoreState();
    221 
    222     pPage->RemovePrivateData((void*) 1);
    223 
    224     delete pContext;
    225 }
    226 
    227 static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr,
    228         jlong bitmapPtr, jint destLeft, jint destTop, jint destRight, jint destBottom,
    229         jlong matrixPtr, jint renderMode) {
    230 
    231     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
    232     FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr);
    233     SkBitmap* skBitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
    234     SkMatrix* skMatrix = reinterpret_cast<SkMatrix*>(matrixPtr);
    235 
    236     skBitmap->lockPixels();
    237 
    238     const int stride = skBitmap->width() * 4;
    239 
    240     FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap->width(), skBitmap->height(),
    241             FPDFBitmap_BGRA, skBitmap->getPixels(), stride);
    242 
    243     if (!bitmap) {
    244         ALOGE("Erorr creating bitmap");
    245         return;
    246     }
    247 
    248     int renderFlags = 0;
    249     if (renderMode == RENDER_MODE_FOR_DISPLAY) {
    250         renderFlags |= FPDF_LCD_TEXT;
    251     } else if (renderMode == RENDER_MODE_FOR_PRINT) {
    252         renderFlags |= FPDF_PRINTING;
    253     }
    254 
    255     renderPageBitmap(bitmap, page, destLeft, destTop, destRight,
    256             destBottom, skMatrix, renderFlags);
    257 
    258     skBitmap->notifyPixelsChanged();
    259     skBitmap->unlockPixels();
    260 }
    261 
    262 static JNINativeMethod gPdfRenderer_Methods[] = {
    263     {"nativeCreate", "(IJ)J", (void*) nativeCreate},
    264     {"nativeClose", "(J)V", (void*) nativeClose},
    265     {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
    266     {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting},
    267     {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage},
    268     {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize},
    269     {"nativeClosePage", "(J)V", (void*) nativeClosePage}
    270 };
    271 
    272 int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) {
    273     int result = android::AndroidRuntime::registerNativeMethods(
    274             env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods,
    275             NELEM(gPdfRenderer_Methods));
    276 
    277     jclass clazz = env->FindClass("android/graphics/Point");
    278     gPointClassInfo.x = env->GetFieldID(clazz, "x", "I");
    279     gPointClassInfo.y = env->GetFieldID(clazz, "y", "I");
    280 
    281     return result;
    282 };
    283 
    284 };
    285