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