Merge "Switch to the new Skia PDF generation APIs." into klp-dev
This commit is contained in:
@@ -17,62 +17,138 @@
|
||||
#include "jni.h"
|
||||
#include "GraphicsJNI.h"
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
#include <vector>
|
||||
|
||||
#include "CreateJavaOutputStreamAdaptor.h"
|
||||
|
||||
#include "SkCanvas.h"
|
||||
#include "SkPDFDevice.h"
|
||||
#include "SkPDFDocument.h"
|
||||
#include "SkDocument.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkSize.h"
|
||||
#include "CreateJavaOutputStreamAdaptor.h"
|
||||
#include "JNIHelp.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
#define LOGD(x...) do { Log::Instance()->printf(Log::ELogD, x); } while(0)
|
||||
struct PageRecord {
|
||||
|
||||
static jint nativeCreateDocument(JNIEnv* env, jobject clazz) {
|
||||
return reinterpret_cast<jint>(new SkPDFDocument());
|
||||
PageRecord(int width, int height, const SkRect& contentRect)
|
||||
: mPicture(new SkPicture()), mWidth(width), mHeight(height) {
|
||||
mContentRect = contentRect;
|
||||
}
|
||||
|
||||
~PageRecord() {
|
||||
mPicture->unref();
|
||||
}
|
||||
|
||||
SkPicture* const mPicture;
|
||||
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);
|
||||
|
||||
SkRect contentRect = SkRect::MakeLTRB(
|
||||
contentLeft, contentTop, contentRight, contentBottom);
|
||||
PageRecord* page = new PageRecord(width, height, contentRect);
|
||||
mPages.push_back(page);
|
||||
mCurrentPage = page;
|
||||
|
||||
SkCanvas* canvas = page->mPicture->beginRecording(
|
||||
contentRect.width(), contentRect.height(), 0);
|
||||
|
||||
// We pass this canvas to Java where it is used to construct
|
||||
// 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;
|
||||
}
|
||||
|
||||
void write(SkWStream* stream) {
|
||||
SkDocument* document = SkDocument::CreatePDF(stream);
|
||||
for (unsigned i = 0; i < mPages.size(); i++) {
|
||||
PageRecord* page = mPages[i];
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void close() {
|
||||
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 void nativeFinalize(JNIEnv* env, jobject thiz, jint documentPtr) {
|
||||
delete reinterpret_cast<SkPDFDocument*>(documentPtr);
|
||||
}
|
||||
|
||||
static jint nativeCreatePage(JNIEnv* env, jobject thiz, jint pageWidth, jint pageHeight,
|
||||
static jint nativeStartPage(JNIEnv* env, jobject thiz, jint documentPtr,
|
||||
jint pageWidth, jint pageHeight,
|
||||
jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
|
||||
|
||||
SkMatrix transformation;
|
||||
transformation.setTranslate(contentLeft, contentTop);
|
||||
|
||||
SkISize skPageSize = SkISize::Make(pageWidth, pageHeight);
|
||||
SkISize skContentSize = SkISize::Make(contentRight - contentLeft, contentBottom - contentTop);
|
||||
|
||||
SkPDFDevice* skPdfDevice = new SkPDFDevice(skPageSize, skContentSize, transformation);
|
||||
return reinterpret_cast<jint>(new SkCanvas(skPdfDevice));
|
||||
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
|
||||
return reinterpret_cast<jint>(document->startPage(pageWidth, pageHeight,
|
||||
contentLeft, contentTop, contentRight, contentBottom));
|
||||
}
|
||||
|
||||
static void nativeAppendPage(JNIEnv* env, jobject thiz, jint documentPtr, jint pagePtr) {
|
||||
SkCanvas* page = reinterpret_cast<SkCanvas*>(pagePtr);
|
||||
SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
|
||||
SkPDFDevice* device = static_cast<SkPDFDevice*>(page->getDevice());
|
||||
document->appendPage(device);
|
||||
static void nativeFinishPage(JNIEnv* env, jobject thiz, jint documentPtr) {
|
||||
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
|
||||
document->finishPage();
|
||||
}
|
||||
|
||||
static void nativeWriteTo(JNIEnv* env, jobject clazz, jint documentPtr,
|
||||
jobject out, jbyteArray chunk) {
|
||||
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);
|
||||
SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
|
||||
document->emitPDF(skWStream);
|
||||
document->write(skWStream);
|
||||
delete skWStream;
|
||||
}
|
||||
|
||||
static void nativeClose(JNIEnv* env, jobject thiz, jint documentPtr) {
|
||||
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
|
||||
document->close();
|
||||
}
|
||||
|
||||
static JNINativeMethod gPdfDocument_Methods[] = {
|
||||
{"nativeCreateDocument", "()I", (void*) nativeCreateDocument},
|
||||
{"nativeFinalize", "(I)V", (void*) nativeFinalize},
|
||||
{"nativeCreatePage", "(IIIIII)I",
|
||||
(void*) nativeCreatePage},
|
||||
{"nativeAppendPage", "(II)V", (void*) nativeAppendPage},
|
||||
{"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo}
|
||||
{"nativeStartPage", "(IIIIIII)I", (void*) nativeStartPage},
|
||||
{"nativeFinishPage", "(I)V", (void*) nativeFinishPage},
|
||||
{"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo},
|
||||
{"nativeClose", "(I)V", (void*) nativeClose}
|
||||
};
|
||||
|
||||
int register_android_graphics_pdf_PdfDocument(JNIEnv* env) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.graphics.pdf;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import dalvik.system.CloseGuard;
|
||||
@@ -69,6 +70,12 @@ import java.util.List;
|
||||
*/
|
||||
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 CloseGuard mCloseGuard = CloseGuard.get();
|
||||
@@ -111,7 +118,7 @@ public class PdfDocument {
|
||||
if (pageInfo == 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.mContentRect.right, pageInfo.mContentRect.bottom));
|
||||
mCurrentPage = new Page(canvas, pageInfo);
|
||||
@@ -142,7 +149,7 @@ public class PdfDocument {
|
||||
}
|
||||
mPages.add(page.getInfo());
|
||||
mCurrentPage = null;
|
||||
nativeAppendPage(mNativeDocument, page.mCanvas.mNativeCanvas);
|
||||
nativeFinishPage(mNativeDocument);
|
||||
page.finish();
|
||||
}
|
||||
|
||||
@@ -204,7 +211,7 @@ public class PdfDocument {
|
||||
|
||||
private void dispose() {
|
||||
if (mNativeDocument != 0) {
|
||||
nativeFinalize(mNativeDocument);
|
||||
nativeClose(mNativeDocument);
|
||||
mCloseGuard.close();
|
||||
mNativeDocument = 0;
|
||||
}
|
||||
@@ -230,14 +237,14 @@ public class PdfDocument {
|
||||
|
||||
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 static native int nativeCreatePage(int pageWidth, int pageHeight, int contentLeft,
|
||||
int contentTop, int contentRight, int contentBottom);
|
||||
private static native int nativeStartPage(int documentPtr, int pageWidth, int pageHeight,
|
||||
int contentLeft, int contentTop, int contentRight, int contentBottom);
|
||||
|
||||
private final class PdfCanvas extends Canvas {
|
||||
|
||||
@@ -392,28 +399,28 @@ public class PdfDocument {
|
||||
* Gets the {@link Canvas} of the page.
|
||||
*
|
||||
* <p>
|
||||
* <strong>Note: </strong> There are some draw operations that are
|
||||
* not yet supported by the canvas returned by this method. More
|
||||
* specifically:
|
||||
* <strong>Note: </strong> There are some draw operations that are not yet
|
||||
* supported by the canvas returned by this method. More specifically:
|
||||
* <ul>
|
||||
* <li>{@link Canvas#clipPath(android.graphics.Path)
|
||||
* Canvas.clipPath(android.graphics.Path)}</li>
|
||||
* <li>All flavors of {@link Canvas#drawText(String, float, float,
|
||||
* android.graphics.Paint) Canvas.drawText(String, float, float,
|
||||
* android.graphics.Paint)}</li>
|
||||
* <li>All flavors of {@link Canvas#drawPosText(String, float[],
|
||||
* android.graphics.Paint) Canvas.drawPosText(String, float[],
|
||||
* android.graphics.Paint)}</li>
|
||||
* <li>Inverse path clipping performed via {@link Canvas#clipPath(android.graphics.Path,
|
||||
* android.graphics.Region.Op) Canvas.clipPath(android.graphics.Path,
|
||||
* android.graphics.Region.Op)} for {@link
|
||||
* android.graphics.Region.Op#REVERSE_DIFFERENCE
|
||||
* Region.Op#REVERSE_DIFFERENCE} operations.</li>
|
||||
* <li>{@link Canvas#drawVertices(android.graphics.Canvas.VertexMode, int,
|
||||
* float[], int, float[], int, int[], int, short[], int, int,
|
||||
* android.graphics.Paint) Canvas.drawVertices(
|
||||
* android.graphics.Canvas.VertexMode, int, float[], int, float[],
|
||||
* 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#XOR PorterDuff.XOR},
|
||||
* {@link android.graphics.PorterDuff.Mode#ADD PorterDuff.ADD}</li>
|
||||
* <li>Perspective transforms</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return The canvas if the page is not finished, null otherwise.
|
||||
|
||||
Reference in New Issue
Block a user