1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  * Copyright (C) 2016 Mopria Alliance, Inc.
4  * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 #include <jni.h>
19 #include <malloc.h>
20 #include "wprint_mupdf.h"
21 #include "wprint_debug.h"
22 
23 #define TAG "pdf_render"
24 
25 /* Global reference to JVM */
26 extern JavaVM *_JVM;
27 
28 /* Local data associated with pdf_render_st instances */
29 typedef struct pdf_render_st {
30     /* Public interface. Must be first. */
31     pdf_render_ifc_t ifc;
32 
33     /* JNI environment */
34     JNIEnv *env;
35 
36     /* true if the env was created for this thread */
37     bool needDetach;
38 
39     /* Reference to associated PdfRender object */
40     jobject obj;
41 } pdf_render_st_t;
42 
43 static jclass gPdfRenderClass;
44 static jmethodID gPdfRenderOpenDocument, gPdfRenderGetPageSize, gPdfRenderRenderPageStripe;
45 static jclass gSizeDClass;
46 static jmethodID gSizeDGetHeight, gSizeDGetWidth;
47 
openDocument(pdf_render_ifc_t * obj,const char * fileName)48 static int openDocument(pdf_render_ifc_t *obj, const char *fileName) {
49     LOGD("getPageCount %p %s", obj, fileName);
50     if (!gPdfRenderClass) return ERROR;
51 
52     pdf_render_st_t *self = (pdf_render_st_t *) obj;
53     jstring fileNameString = (*self->env)->NewStringUTF(self->env, fileName);
54     int count = (*self->env)->CallIntMethod(self->env, self->obj, gPdfRenderOpenDocument,
55             fileNameString);
56     LOGD("getPageCount %p %s returning %d", obj, fileName, count);
57     return count;
58 }
59 
getPageAttributes(pdf_render_ifc_t * obj,int page,double * width,double * height)60 static int getPageAttributes(pdf_render_ifc_t *obj, int page, double *width, double *height) {
61     LOGD("getPageAttributes %p %d", obj, page);
62     if (!gPdfRenderClass) return ERROR;
63 
64     pdf_render_st_t *self = (pdf_render_st_t *) obj;
65 
66     jobject size = (*self->env)->CallObjectMethod(self->env, self->obj, gPdfRenderGetPageSize,
67             page);
68     if (size == NULL) return ERROR;
69 
70     // Extract width/height and return them
71     *width = (double) (*self->env)->CallDoubleMethod(self->env, size, gSizeDGetWidth);
72     *height = (double) (*self->env)->CallDoubleMethod(self->env, size, gSizeDGetHeight);
73     return OK;
74 }
75 
renderPageStripe(pdf_render_ifc_t * obj,int page,int width,int height,float zoom,char * buffer)76 static int renderPageStripe(pdf_render_ifc_t *obj, int page, int width, int height, float zoom,
77         char *buffer) {
78     LOGD("renderPageStripe %p %d", obj, page);
79     if (!gPdfRenderClass) return ERROR;
80 
81     pdf_render_st_t *self = (pdf_render_st_t *) obj;
82 
83     int bufferSize = width * height * 3;
84     jobject byteBuffer = (*self->env)->NewDirectByteBuffer(self->env, buffer, bufferSize);
85 
86     if (!(*self->env)->CallBooleanMethod(self->env, self->obj, gPdfRenderRenderPageStripe, page,
87             0, width, height, (double) zoom, byteBuffer)) {
88         return ERROR;
89     }
90 
91     (*self->env)->DeleteLocalRef(self->env, byteBuffer);
92     return OK;
93 }
94 
destroy(pdf_render_ifc_t * obj)95 static void destroy(pdf_render_ifc_t *obj) {
96     LOGD("destroy %p", obj);
97     pdf_render_st_t *self = (pdf_render_st_t *) obj;
98 
99     (*self->env)->DeleteGlobalRef(self->env, self->obj);
100 
101     if (self->needDetach) {
102         (*_JVM)->DetachCurrentThread(_JVM);
103     }
104 
105     free(self);
106 }
107 
pdf_render_init(JNIEnv * env)108 void pdf_render_init(JNIEnv *env) {
109     LOGD("pdf_render_init");
110 
111     /* Lock down global class references and look up method IDs */
112     gPdfRenderClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env,
113             "com/android/bips/jni/PdfRender"));
114     gPdfRenderOpenDocument = (*env)->GetMethodID(env, gPdfRenderClass, "openDocument",
115             "(Ljava/lang/String;)I");
116     gPdfRenderGetPageSize = (*env)->GetMethodID(env, gPdfRenderClass, "getPageSize",
117             "(I)Lcom/android/bips/jni/SizeD;");
118     gPdfRenderRenderPageStripe = (*env)->GetMethodID(env, gPdfRenderClass, "renderPageStripe",
119             "(IIIIDLjava/nio/ByteBuffer;)Z");
120 
121     gSizeDClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/android/bips/jni/SizeD"));
122     gSizeDGetWidth = (*env)->GetMethodID(env, gSizeDClass, "getWidth", "()D");
123     gSizeDGetHeight = (*env)->GetMethodID(env, gSizeDClass, "getHeight", "()D");
124 }
125 
pdf_render_deinit(JNIEnv * env)126 void pdf_render_deinit(JNIEnv *env) {
127     LOGD("pdf_render_deinit");
128     (*env)->DeleteGlobalRef(env, gPdfRenderClass);
129     (*env)->DeleteGlobalRef(env, gSizeDClass);
130     gPdfRenderClass = 0;
131 }
132 
create_pdf_render_ifc()133 pdf_render_ifc_t *create_pdf_render_ifc() {
134     LOGD("create_pdf_render_ifc");
135 
136     pdf_render_st_t *self;
137 
138     // Set up the interface
139     self = (pdf_render_st_t *) malloc(sizeof(pdf_render_st_t));
140     if (!self) return NULL;
141 
142     self->ifc.openDocument = openDocument;
143     self->ifc.getPageAttributes = getPageAttributes;
144     self->ifc.renderPageStripe = renderPageStripe;
145     self->ifc.destroy = destroy;
146 
147     // Get the environment
148     jint result = (*_JVM)->GetEnv(_JVM, (void **) &self->env, JNI_VERSION_1_6);
149     if (result == JNI_EDETACHED) {
150         self->needDetach = true;
151         if ((*_JVM)->AttachCurrentThread(_JVM, &self->env, NULL) < 0) {
152             LOGE("AttachCurrentThread failed");
153             free(self);
154             return NULL;
155         }
156     } else {
157         self->needDetach = false;
158     }
159 
160     // Get the object
161     jmethodID methodId = (*self->env)->GetStaticMethodID(self->env, gPdfRenderClass, "getInstance",
162             "(Landroid/content/Context;)Lcom/android/bips/jni/PdfRender;");
163     jobject instance = (*self->env)->CallStaticObjectMethod(self->env, gPdfRenderClass, methodId,
164             NULL);
165     self->obj = (*self->env)->NewGlobalRef(self->env, instance);
166 
167     return &self->ifc;
168 }