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
32namespace android {
33
34struct PageRecord {
35
36    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
44    ~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
58class PdfDocument {
59public:
60    PdfDocument() {
61        mCurrentPage = NULL;
62    }
63
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
80    void finishPage() {
81        assert(mCurrentPage != NULL);
82        assert(mCurrentPage->mPictureRecorder != NULL);
83        assert(mCurrentPage->mPicture == NULL);
84        mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->endRecording();
85        delete mCurrentPage->mPictureRecorder;
86        mCurrentPage->mPictureRecorder = NULL;
87        mCurrentPage = NULL;
88    }
89
90    void write(SkWStream* stream) {
91        SkAutoTUnref<SkDocument> document(SkDocument::CreatePDF(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
98            canvas->drawPicture(page->mPicture);
99
100            document->endPage();
101        }
102        document->close();
103    }
104
105    void close() {
106        assert(NULL == mCurrentPage);
107        for (unsigned i = 0; i < mPages.size(); i++) {
108            delete mPages[i];
109        }
110    }
111
112private:
113    ~PdfDocument() {
114        close();
115    }
116
117    std::vector<PageRecord*> mPages;
118    PageRecord* mCurrentPage;
119};
120
121static jlong nativeCreateDocument(JNIEnv* env, jobject thiz) {
122    return reinterpret_cast<jlong>(new PdfDocument());
123}
124
125static jlong nativeStartPage(JNIEnv* env, jobject thiz, jlong documentPtr,
126        jint pageWidth, jint pageHeight,
127        jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
128    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
129    SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
130            contentLeft, contentTop, contentRight, contentBottom);
131    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
132}
133
134static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
135    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
136    document->finishPage();
137}
138
139static void nativeWriteTo(JNIEnv* env, jobject thiz, jlong documentPtr, jobject out,
140        jbyteArray chunk) {
141    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
142    SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
143    document->write(skWStream);
144    delete skWStream;
145}
146
147static void nativeClose(JNIEnv* env, jobject thiz, jlong documentPtr) {
148    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
149    document->close();
150}
151
152static const JNINativeMethod gPdfDocument_Methods[] = {
153    {"nativeCreateDocument", "()J", (void*) nativeCreateDocument},
154    {"nativeStartPage", "(JIIIIII)J", (void*) nativeStartPage},
155    {"nativeFinishPage", "(J)V", (void*) nativeFinishPage},
156    {"nativeWriteTo", "(JLjava/io/OutputStream;[B)V", (void*) nativeWriteTo},
157    {"nativeClose", "(J)V", (void*) nativeClose}
158};
159
160int register_android_graphics_pdf_PdfDocument(JNIEnv* env) {
161    return RegisterMethodsOrDie(
162            env, "android/graphics/pdf/PdfDocument", gPdfDocument_Methods,
163            NELEM(gPdfDocument_Methods));
164}
165
166};
167