Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35aacf2e authored by Svetoslav's avatar Svetoslav Committed by Svetoslav Ganov
Browse files

Switch to the new Skia PDF generation APIs.

The new Skia PDF generation APIs are a small extension to
the code that converts drawing commands to PDF (SkPDFDevice)
and this new functionality is exposed via new APIs. This
change switches to using these new APIs allowing us to
capitalize on the new perspective support for PDF
generation.

bug:11561776

Change-Id: Ief61f7ff6a5a22c27d3acbe99a48910cb679f594
parent c157cac9
Loading
Loading
Loading
Loading
+110 −34
Original line number Original line Diff line number Diff line
@@ -17,62 +17,138 @@
#include "jni.h"
#include "jni.h"
#include "GraphicsJNI.h"
#include "GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/AndroidRuntime.h>
#include <vector>

#include "CreateJavaOutputStreamAdaptor.h"


#include "SkCanvas.h"
#include "SkCanvas.h"
#include "SkPDFDevice.h"
#include "SkDocument.h"
#include "SkPDFDocument.h"
#include "SkPicture.h"
#include "SkStream.h"
#include "SkRect.h"
#include "SkRect.h"
#include "SkSize.h"
#include "CreateJavaOutputStreamAdaptor.h"
#include "JNIHelp.h"


namespace android {
namespace android {


#define LOGD(x...) do { Log::Instance()->printf(Log::ELogD, x); } while(0)
struct PageRecord {


static jint nativeCreateDocument(JNIEnv* env, jobject clazz) {
    PageRecord(int width, int height, const SkRect& contentRect)
    return reinterpret_cast<jint>(new SkPDFDocument());
            : mPicture(new SkPicture()), mWidth(width), mHeight(height) {
        mContentRect = contentRect;
    }
    }


static void nativeFinalize(JNIEnv* env, jobject thiz, jint documentPtr) {
    ~PageRecord() {
    delete reinterpret_cast<SkPDFDocument*>(documentPtr);
        mPicture->unref();
    }
    }


static jint nativeCreatePage(JNIEnv* env, jobject thiz, jint pageWidth, jint pageHeight,
    SkPicture* const mPicture;
        jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
    const int mWidth;
    const int mHeight;
    SkRect mContentRect;
};

class PdfDocument {
public:
    PdfDocument() {
        mCurrentPage = NULL;
    }

    SkCanvas* startPage(int width, int height,
            int contentLeft, int contentTop, int contentRight, int contentBottom) {
        assert(mCurrentPage == NULL);


    SkMatrix transformation;
        SkRect contentRect = SkRect::MakeLTRB(
    transformation.setTranslate(contentLeft, contentTop);
                contentLeft, contentTop, contentRight, contentBottom);
        PageRecord* page = new PageRecord(width, height, contentRect);
        mPages.push_back(page);
        mCurrentPage = page;


    SkISize skPageSize = SkISize::Make(pageWidth, pageHeight);
        SkCanvas* canvas = page->mPicture->beginRecording(
    SkISize skContentSize = SkISize::Make(contentRight - contentLeft, contentBottom - contentTop);
                contentRect.width(), contentRect.height(), 0);


    SkPDFDevice* skPdfDevice = new SkPDFDevice(skPageSize, skContentSize, transformation);
        // We pass this canvas to Java where it is used to construct
    return reinterpret_cast<jint>(new SkCanvas(skPdfDevice));
        // a Java Canvas object which dereferences the pointer when it
        // is destroyed, so we have to bump up the reference count.
        canvas->ref();

        return canvas;
    }

    void finishPage() {
        assert(mCurrentPage != NULL);
        mCurrentPage->mPicture->endRecording();
        mCurrentPage = NULL;
    }
    }


static void nativeAppendPage(JNIEnv* env, jobject thiz, jint documentPtr, jint pagePtr) {
    void write(SkWStream* stream) {
    SkCanvas* page = reinterpret_cast<SkCanvas*>(pagePtr);
        SkDocument* document = SkDocument::CreatePDF(stream);
    SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
        for (unsigned i = 0; i < mPages.size(); i++) {
    SkPDFDevice* device = static_cast<SkPDFDevice*>(page->getDevice());
            PageRecord* page =  mPages[i];
    document->appendPage(device);

            SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
                    &(page->mContentRect));

            canvas->clipRect(page->mContentRect);
            canvas->translate(page->mContentRect.left(), page->mContentRect.top());
            canvas->drawPicture(*page->mPicture);

            document->endPage();
        }
        document->close();
    }
    }


static void nativeWriteTo(JNIEnv* env, jobject clazz, jint documentPtr,
    void close() {
        jobject out, jbyteArray chunk) {
        for (unsigned i = 0; i < mPages.size(); i++) {
            delete mPages[i];
        }
        delete mCurrentPage;
        mCurrentPage = NULL;
    }

private:
    ~PdfDocument() {
        close();
    }

    std::vector<PageRecord*> mPages;
    PageRecord* mCurrentPage;
};

static jint nativeCreateDocument(JNIEnv* env, jobject thiz) {
    return reinterpret_cast<jint>(new PdfDocument());
}

static jint nativeStartPage(JNIEnv* env, jobject thiz, jint documentPtr,
        jint pageWidth, jint pageHeight,
        jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
    return reinterpret_cast<jint>(document->startPage(pageWidth, pageHeight,
            contentLeft, contentTop, contentRight, contentBottom));
}

static void nativeFinishPage(JNIEnv* env, jobject thiz, jint documentPtr) {
    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
    document->finishPage();
}

static void nativeWriteTo(JNIEnv* env, jobject thiz, jint documentPtr, jobject out,
        jbyteArray chunk) {
    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
    SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
    SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
    SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
    document->write(skWStream);
    document->emitPDF(skWStream);
    delete skWStream;
    delete skWStream;
}
}


static void nativeClose(JNIEnv* env, jobject thiz, jint documentPtr) {
    PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
    document->close();
}

static JNINativeMethod gPdfDocument_Methods[] = {
static JNINativeMethod gPdfDocument_Methods[] = {
    {"nativeCreateDocument", "()I", (void*) nativeCreateDocument},
    {"nativeCreateDocument", "()I", (void*) nativeCreateDocument},
    {"nativeFinalize", "(I)V", (void*) nativeFinalize},
    {"nativeStartPage", "(IIIIIII)I", (void*) nativeStartPage},
    {"nativeCreatePage", "(IIIIII)I",
    {"nativeFinishPage", "(I)V", (void*) nativeFinishPage},
            (void*) nativeCreatePage},
    {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo},
    {"nativeAppendPage", "(II)V", (void*) nativeAppendPage},
    {"nativeClose", "(I)V", (void*) nativeClose}
    {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo}
};
};


int register_android_graphics_pdf_PdfDocument(JNIEnv* env) {
int register_android_graphics_pdf_PdfDocument(JNIEnv* env) {
+27 −20
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.graphics.pdf;


import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Rect;


import dalvik.system.CloseGuard;
import dalvik.system.CloseGuard;
@@ -69,6 +70,12 @@ import java.util.List;
 */
 */
public class PdfDocument {
public class PdfDocument {


    // TODO: We need a constructor that will take an OutputStream to
    // support online data serialization as opposed to the current
    // on demand one. The current approach is fine until Skia starts
    // to support online PDF generation at which point we need to
    // handle this.

    private final byte[] mChunk = new byte[4096];
    private final byte[] mChunk = new byte[4096];


    private final CloseGuard mCloseGuard = CloseGuard.get();
    private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -111,7 +118,7 @@ public class PdfDocument {
        if (pageInfo == null) {
        if (pageInfo == null) {
            throw new IllegalArgumentException("page cannot be null");
            throw new IllegalArgumentException("page cannot be null");
        }
        }
        Canvas canvas = new PdfCanvas(nativeCreatePage(pageInfo.mPageWidth,
        Canvas canvas = new PdfCanvas(nativeStartPage(mNativeDocument, pageInfo.mPageWidth,
                pageInfo.mPageHeight, pageInfo.mContentRect.left, pageInfo.mContentRect.top,
                pageInfo.mPageHeight, pageInfo.mContentRect.left, pageInfo.mContentRect.top,
                pageInfo.mContentRect.right, pageInfo.mContentRect.bottom));
                pageInfo.mContentRect.right, pageInfo.mContentRect.bottom));
        mCurrentPage = new Page(canvas, pageInfo);
        mCurrentPage = new Page(canvas, pageInfo);
@@ -142,7 +149,7 @@ public class PdfDocument {
        }
        }
        mPages.add(page.getInfo());
        mPages.add(page.getInfo());
        mCurrentPage = null;
        mCurrentPage = null;
        nativeAppendPage(mNativeDocument, page.mCanvas.mNativeCanvas);
        nativeFinishPage(mNativeDocument);
        page.finish();
        page.finish();
    }
    }


@@ -204,7 +211,7 @@ public class PdfDocument {


    private void dispose() {
    private void dispose() {
        if (mNativeDocument != 0) {
        if (mNativeDocument != 0) {
            nativeFinalize(mNativeDocument);
            nativeClose(mNativeDocument);
            mCloseGuard.close();
            mCloseGuard.close();
            mNativeDocument = 0;
            mNativeDocument = 0;
        }
        }
@@ -230,14 +237,14 @@ public class PdfDocument {


    private native int nativeCreateDocument();
    private native int nativeCreateDocument();


    private native void nativeFinalize(int document);
    private native void nativeClose(int document);


    private native void nativeAppendPage(int document, int page);
    private native void nativeFinishPage(int document);


    private native void nativeWriteTo(int document, OutputStream out, byte[] chunk);
    private native void nativeWriteTo(int document, OutputStream out, byte[] chunk);


    private static native int nativeCreatePage(int pageWidth, int pageHeight, int contentLeft,
    private static native int nativeStartPage(int documentPtr, int pageWidth, int pageHeight,
            int contentTop, int contentRight, int contentBottom);
            int contentLeft, int contentTop, int contentRight, int contentBottom);


    private final class PdfCanvas extends Canvas {
    private final class PdfCanvas extends Canvas {


@@ -392,28 +399,28 @@ public class PdfDocument {
         * Gets the {@link Canvas} of the page.
         * Gets the {@link Canvas} of the page.
         *
         *
         * <p>
         * <p>
         * <strong>Note: </strong> There are some draw operations that are
         * <strong>Note: </strong> There are some draw operations that are not yet
         * not yet supported by the canvas returned by this method. More
         * supported by the canvas returned by this method. More specifically:
         * specifically:
         * <ul>
         * <ul>
         * <li>{@link Canvas#clipPath(android.graphics.Path)
         * <li>Inverse path clipping performed via {@link Canvas#clipPath(android.graphics.Path,
         *     Canvas.clipPath(android.graphics.Path)}</li>
         *     android.graphics.Region.Op) Canvas.clipPath(android.graphics.Path,
         * <li>All flavors of {@link Canvas#drawText(String, float, float,
         *     android.graphics.Region.Op)} for {@link
         *     android.graphics.Paint) Canvas.drawText(String, float, float,
         *     android.graphics.Region.Op#REVERSE_DIFFERENCE
         *     android.graphics.Paint)}</li>
         *     Region.Op#REVERSE_DIFFERENCE} operations.</li>
         * <li>All flavors of {@link Canvas#drawPosText(String, float[],
         *     android.graphics.Paint) Canvas.drawPosText(String, float[],
         *     android.graphics.Paint)}</li>
         * <li>{@link Canvas#drawVertices(android.graphics.Canvas.VertexMode, int,
         * <li>{@link Canvas#drawVertices(android.graphics.Canvas.VertexMode, int,
         *     float[], int, float[], int, int[], int, short[], int, int,
         *     float[], int, float[], int, int[], int, short[], int, int,
         *     android.graphics.Paint) Canvas.drawVertices(
         *     android.graphics.Paint) Canvas.drawVertices(
         *     android.graphics.Canvas.VertexMode, int, float[], int, float[],
         *     android.graphics.Canvas.VertexMode, int, float[], int, float[],
         *     int, int[], int, short[], int, int, android.graphics.Paint)}</li>
         *     int, int[], int, short[], int, int, android.graphics.Paint)}</li>
         * <li>{@link android.graphics.PorterDuff.Mode#SRC_ATOP PorterDuff.Mode SRC},
         * <li>Color filters set via {@link Paint#setColorFilter(
         *     android.graphics.ColorFilter)}</li>
         * <li>Mask filters set via {@link Paint#setMaskFilter(
         *     android.graphics.MaskFilter)}</li>
         * <li>Some XFER modes such as
         *     {@link android.graphics.PorterDuff.Mode#SRC_ATOP PorterDuff.Mode SRC},
         *     {@link android.graphics.PorterDuff.Mode#DST_ATOP PorterDuff.DST_ATOP},
         *     {@link android.graphics.PorterDuff.Mode#DST_ATOP PorterDuff.DST_ATOP},
         *     {@link android.graphics.PorterDuff.Mode#XOR PorterDuff.XOR},
         *     {@link android.graphics.PorterDuff.Mode#XOR PorterDuff.XOR},
         *     {@link android.graphics.PorterDuff.Mode#ADD PorterDuff.ADD}</li>
         *     {@link android.graphics.PorterDuff.Mode#ADD PorterDuff.ADD}</li>
         * <li>Perspective transforms</li>
         * </ul>
         * </ul>
         *
         *
         * @return The canvas if the page is not finished, null otherwise.
         * @return The canvas if the page is not finished, null otherwise.