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 "PdfUtils.h"
     18 
     19 #include "jni.h"
     20 #include <nativehelper/JNIHelp.h>
     21 #include "GraphicsJNI.h"
     22 #include "SkBitmap.h"
     23 #include "SkMatrix.h"
     24 #include "fpdfview.h"
     25 
     26 #include "core_jni_helpers.h"
     27 #include <vector>
     28 #include <utils/Log.h>
     29 #include <unistd.h>
     30 #include <sys/types.h>
     31 #include <unistd.h>
     32 
     33 namespace android {
     34 
     35 static const int RENDER_MODE_FOR_DISPLAY = 1;
     36 static const int RENDER_MODE_FOR_PRINT = 2;
     37 
     38 static struct {
     39     jfieldID x;
     40     jfieldID y;
     41 } gPointClassInfo;
     42 
     43 static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr,
     44         jint pageIndex, jobject outSize) {
     45     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
     46 
     47     FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
     48     if (!page) {
     49         jniThrowException(env, "java/lang/IllegalStateException",
     50                 "cannot load page");
     51         return -1;
     52     }
     53     HANDLE_PDFIUM_ERROR_STATE_WITH_RET_CODE(env, -1)
     54 
     55     double width = 0;
     56     double height = 0;
     57 
     58     int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height);
     59     if (!result) {
     60         jniThrowException(env, "java/lang/IllegalStateException",
     61                     "cannot get page size");
     62         return -1;
     63     }
     64     HANDLE_PDFIUM_ERROR_STATE_WITH_RET_CODE(env, -1)
     65 
     66     env->SetIntField(outSize, gPointClassInfo.x, width);
     67     env->SetIntField(outSize, gPointClassInfo.y, height);
     68 
     69     return reinterpret_cast<jlong>(page);
     70 }
     71 
     72 static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) {
     73     FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr);
     74     FPDF_ClosePage(page);
     75     HANDLE_PDFIUM_ERROR_STATE(env)
     76 }
     77 
     78 static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr,
     79         jobject jbitmap, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom,
     80         jlong transformPtr, jint renderMode) {
     81     FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr);
     82 
     83     SkBitmap skBitmap;
     84     GraphicsJNI::getSkBitmap(env, jbitmap, &skBitmap);
     85 
     86     const int stride = skBitmap.width() * 4;
     87 
     88     FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap.width(), skBitmap.height(),
     89             FPDFBitmap_BGRA, skBitmap.getPixels(), stride);
     90     bool isExceptionPending = forwardPdfiumError(env);
     91     if (isExceptionPending || bitmap == NULL) {
     92         ALOGE("Error creating bitmap");
     93         return;
     94     }
     95 
     96     int renderFlags = FPDF_REVERSE_BYTE_ORDER;
     97     if (renderMode == RENDER_MODE_FOR_DISPLAY) {
     98         renderFlags |= FPDF_LCD_TEXT;
     99     } else if (renderMode == RENDER_MODE_FOR_PRINT) {
    100         renderFlags |= FPDF_PRINTING;
    101     }
    102 
    103     // PDF's coordinate system origin is left-bottom while in graphics it
    104     // is the top-left. So, translate the PDF coordinates to ours.
    105     SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1);
    106     SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page));
    107     SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX);
    108 
    109     // Apply the transformation
    110     SkMatrix matrix;
    111     if (transformPtr == 0) {
    112         matrix = coordinateChange;
    113     } else {
    114         matrix = SkMatrix::Concat(*reinterpret_cast<SkMatrix*>(transformPtr), coordinateChange);
    115     }
    116 
    117     SkScalar transformValues[6];
    118     if (!matrix.asAffine(transformValues)) {
    119         jniThrowException(env, "java/lang/IllegalArgumentException",
    120                 "transform matrix has perspective. Only affine matrices are allowed.");
    121         return;
    122     }
    123 
    124     FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY],
    125                            transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY],
    126                            transformValues[SkMatrix::kATransX],
    127                            transformValues[SkMatrix::kATransY]};
    128 
    129     FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom};
    130 
    131     FPDF_RenderPageBitmapWithMatrix(bitmap, page, &transform, &clip, renderFlags);
    132     HANDLE_PDFIUM_ERROR_STATE(env);
    133 
    134     skBitmap.notifyPixelsChanged();
    135 }
    136 
    137 static const JNINativeMethod gPdfRenderer_Methods[] = {
    138     {"nativeCreate", "(IJ)J", (void*) nativeOpen},
    139     {"nativeClose", "(J)V", (void*) nativeClose},
    140     {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount},
    141     {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting},
    142     {"nativeRenderPage", "(JJLandroid/graphics/Bitmap;IIIIJI)V", (void*) nativeRenderPage},
    143     {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize},
    144     {"nativeClosePage", "(J)V", (void*) nativeClosePage}
    145 };
    146 
    147 int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) {
    148     int result = RegisterMethodsOrDie(
    149             env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods,
    150             NELEM(gPdfRenderer_Methods));
    151 
    152     jclass clazz = FindClassOrDie(env, "android/graphics/Point");
    153     gPointClassInfo.x = GetFieldIDOrDie(env, clazz, "x", "I");
    154     gPointClassInfo.y = GetFieldIDOrDie(env, clazz, "y", "I");
    155 
    156     return result;
    157 };
    158 
    159 };
    160