1 /*
2  * Copyright (C) 2013 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 "GraphicsJNI.h"
19 #include "core_jni_helpers.h"
20 #include <vector>
21 
22 #include "CreateJavaOutputStreamAdaptor.h"
23 
24 #include "SkDocument.h"
25 #include "SkPicture.h"
26 #include "SkPictureRecorder.h"
27 #include "SkStream.h"
28 #include "SkRect.h"
29 
30 #include <hwui/Canvas.h>
31 
32 namespace android {
33 
34 struct PageRecord {
35 
PageRecordandroid::PageRecord36     PageRecord(int width, int height, const SkRect& contentRect)
37             : mPictureRecorder(new SkPictureRecorder())
38             , mPicture(NULL)
39             , mWidth(width)
40             , mHeight(height) {
41         mContentRect = contentRect;
42     }
43 
~PageRecordandroid::PageRecord44     ~PageRecord() {
45         delete mPictureRecorder;
46         if (NULL != mPicture) {
47             mPicture->unref();
48         }
49     }
50 
51     SkPictureRecorder* mPictureRecorder;
52     SkPicture* mPicture;
53     const int mWidth;
54     const int mHeight;
55     SkRect mContentRect;
56 };
57 
58 class PdfDocument {
59 public:
PdfDocument()60     PdfDocument() {
61         mCurrentPage = NULL;
62     }
63 
startPage(int width,int height,int contentLeft,int contentTop,int contentRight,int contentBottom)64     SkCanvas* startPage(int width, int height,
65             int contentLeft, int contentTop, int contentRight, int contentBottom) {
66         assert(mCurrentPage == NULL);
67 
68         SkRect contentRect = SkRect::MakeLTRB(
69                 contentLeft, contentTop, contentRight, contentBottom);
70         PageRecord* page = new PageRecord(width, height, contentRect);
71         mPages.push_back(page);
72         mCurrentPage = page;
73 
74         SkCanvas* canvas = page->mPictureRecorder->beginRecording(
75                 SkRect::MakeWH(contentRect.width(), contentRect.height()));
76 
77         return canvas;
78     }
79 
finishPage()80     void finishPage() {
81         assert(mCurrentPage != NULL);
82         assert(mCurrentPage->mPictureRecorder != NULL);
83         assert(mCurrentPage->mPicture == NULL);
84         mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->finishRecordingAsPicture().release();
85         delete mCurrentPage->mPictureRecorder;
86         mCurrentPage->mPictureRecorder = NULL;
87         mCurrentPage = NULL;
88     }
89 
write(SkWStream * stream)90     void write(SkWStream* stream) {
91         sk_sp<SkDocument> document = SkDocument::MakePDF(stream);
92         for (unsigned i = 0; i < mPages.size(); i++) {
93             PageRecord* page =  mPages[i];
94 
95             SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
96                     &(page->mContentRect));
97             canvas->drawPicture(page->mPicture);
98 
99             document->endPage();
100         }
101         document->close();
102     }
103 
close()104     void close() {
105         assert(NULL == mCurrentPage);
106         for (unsigned i = 0; i < mPages.size(); i++) {
107             delete mPages[i];
108         }
109     }
110 
111 private:
~PdfDocument()112     ~PdfDocument() {
113         close();
114     }
115 
116     std::vector<PageRecord*> mPages;
117     PageRecord* mCurrentPage;
118 };
119 
nativeCreateDocument(JNIEnv * env,jobject thiz)120 static jlong nativeCreateDocument(JNIEnv* env, jobject thiz) {
121     return reinterpret_cast<jlong>(new PdfDocument());
122 }
123 
nativeStartPage(JNIEnv * env,jobject thiz,jlong documentPtr,jint pageWidth,jint pageHeight,jint contentLeft,jint contentTop,jint contentRight,jint contentBottom)124 static jlong nativeStartPage(JNIEnv* env, jobject thiz, jlong documentPtr,
125         jint pageWidth, jint pageHeight,
126         jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
127     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
128     SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
129             contentLeft, contentTop, contentRight, contentBottom);
130     return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
131 }
132 
nativeFinishPage(JNIEnv * env,jobject thiz,jlong documentPtr)133 static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
134     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
135     document->finishPage();
136 }
137 
nativeWriteTo(JNIEnv * env,jobject thiz,jlong documentPtr,jobject out,jbyteArray chunk)138 static void nativeWriteTo(JNIEnv* env, jobject thiz, jlong documentPtr, jobject out,
139         jbyteArray chunk) {
140     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
141     SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
142     document->write(skWStream);
143     delete skWStream;
144 }
145 
nativeClose(JNIEnv * env,jobject thiz,jlong documentPtr)146 static void nativeClose(JNIEnv* env, jobject thiz, jlong documentPtr) {
147     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
148     document->close();
149 }
150 
151 static const JNINativeMethod gPdfDocument_Methods[] = {
152     {"nativeCreateDocument", "()J", (void*) nativeCreateDocument},
153     {"nativeStartPage", "(JIIIIII)J", (void*) nativeStartPage},
154     {"nativeFinishPage", "(J)V", (void*) nativeFinishPage},
155     {"nativeWriteTo", "(JLjava/io/OutputStream;[B)V", (void*) nativeWriteTo},
156     {"nativeClose", "(J)V", (void*) nativeClose}
157 };
158 
register_android_graphics_pdf_PdfDocument(JNIEnv * env)159 int register_android_graphics_pdf_PdfDocument(JNIEnv* env) {
160     return RegisterMethodsOrDie(
161             env, "android/graphics/pdf/PdfDocument", gPdfDocument_Methods,
162             NELEM(gPdfDocument_Methods));
163 }
164 
165 };
166