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