diff options
| -rw-r--r-- | core/api/current.txt | 18 | ||||
| -rw-r--r-- | graphics/java/android/graphics/pdf/PdfEditor.java | 32 | ||||
| -rw-r--r-- | graphics/java/android/graphics/pdf/PdfRenderer.java | 502 | ||||
| -rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
| -rw-r--r-- | libs/hwui/apex/jni_runtime.cpp | 2 | ||||
| -rw-r--r-- | libs/hwui/jni/pdf/PdfRenderer.cpp | 134 |
6 files changed, 669 insertions, 20 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 33d171239b7e..a3775b05cf2e 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -17922,6 +17922,24 @@ package android.graphics.pdf { method public android.graphics.pdf.PdfDocument.PageInfo.Builder setContentRect(android.graphics.Rect); } + public final class PdfRenderer implements java.lang.AutoCloseable { + ctor public PdfRenderer(@NonNull android.os.ParcelFileDescriptor) throws java.io.IOException; + method public void close(); + method public int getPageCount(); + method public android.graphics.pdf.PdfRenderer.Page openPage(int); + method public boolean shouldScaleForPrinting(); + } + + public final class PdfRenderer.Page implements java.lang.AutoCloseable { + method public void close(); + method public int getHeight(); + method public int getIndex(); + method public int getWidth(); + method public void render(@NonNull android.graphics.Bitmap, @Nullable android.graphics.Rect, @Nullable android.graphics.Matrix, int); + field public static final int RENDER_MODE_FOR_DISPLAY = 1; // 0x1 + field public static final int RENDER_MODE_FOR_PRINT = 2; // 0x2 + } + } package android.graphics.text { diff --git a/graphics/java/android/graphics/pdf/PdfEditor.java b/graphics/java/android/graphics/pdf/PdfEditor.java index 69e19824da26..3cd709ea10e5 100644 --- a/graphics/java/android/graphics/pdf/PdfEditor.java +++ b/graphics/java/android/graphics/pdf/PdfEditor.java @@ -25,9 +25,7 @@ import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; - import dalvik.system.CloseGuard; - import libcore.io.IoUtils; import java.io.IOException; @@ -39,12 +37,6 @@ import java.io.IOException; */ public final class PdfEditor { - /** - * Any call the native pdfium code has to be single threaded as the library does not support - * parallel use. - */ - private static final Object sPdfiumLock = new Object(); - private final CloseGuard mCloseGuard = CloseGuard.get(); private long mNativeDocument; @@ -87,7 +79,7 @@ public final class PdfEditor { } mInput = input; - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { mNativeDocument = nativeOpen(mInput.getFd(), size); try { mPageCount = nativeGetPageCount(mNativeDocument); @@ -120,7 +112,7 @@ public final class PdfEditor { throwIfClosed(); throwIfPageNotInDocument(pageIndex); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { mPageCount = nativeRemovePage(mNativeDocument, pageIndex); } } @@ -146,12 +138,12 @@ public final class PdfEditor { Point size = new Point(); getPageSize(pageIndex, size); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.ni(), 0, 0, size.x, size.y); } } else { - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.ni(), clip.left, clip.top, clip.right, clip.bottom); } @@ -169,7 +161,7 @@ public final class PdfEditor { throwIfOutSizeNull(outSize); throwIfPageNotInDocument(pageIndex); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { nativeGetPageSize(mNativeDocument, pageIndex, outSize); } } @@ -185,7 +177,7 @@ public final class PdfEditor { throwIfOutMediaBoxNull(outMediaBox); throwIfPageNotInDocument(pageIndex); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox); } } @@ -201,7 +193,7 @@ public final class PdfEditor { throwIfMediaBoxNull(mediaBox); throwIfPageNotInDocument(pageIndex); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox); } } @@ -217,7 +209,7 @@ public final class PdfEditor { throwIfOutCropBoxNull(outCropBox); throwIfPageNotInDocument(pageIndex); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox); } } @@ -233,7 +225,7 @@ public final class PdfEditor { throwIfCropBoxNull(cropBox); throwIfPageNotInDocument(pageIndex); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox); } } @@ -246,7 +238,7 @@ public final class PdfEditor { public boolean shouldScaleForPrinting() { throwIfClosed(); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { return nativeScaleForPrinting(mNativeDocument); } } @@ -263,7 +255,7 @@ public final class PdfEditor { try { throwIfClosed(); - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { nativeWrite(mNativeDocument, output.getFd()); } } finally { @@ -295,7 +287,7 @@ public final class PdfEditor { private void doClose() { if (mNativeDocument != 0) { - synchronized (sPdfiumLock) { + synchronized (PdfRenderer.sPdfiumLock) { nativeClose(mNativeDocument); } mNativeDocument = 0; diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java new file mode 100644 index 000000000000..4666963b5dd4 --- /dev/null +++ b/graphics/java/android/graphics/pdf/PdfRenderer.java @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.pdf; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Build; +import android.os.ParcelFileDescriptor; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; + +import com.android.internal.util.Preconditions; + +import dalvik.system.CloseGuard; + +import libcore.io.IoUtils; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * <p> + * This class enables rendering a PDF document. This class is not thread safe. + * </p> + * <p> + * If you want to render a PDF, you create a renderer and for every page you want + * to render, you open the page, render it, and close the page. After you are done + * with rendering, you close the renderer. After the renderer is closed it should not + * be used anymore. Note that the pages are rendered one by one, i.e. you can have + * only a single page opened at any given time. + * </p> + * <p> + * A typical use of the APIs to render a PDF looks like this: + * </p> + * <pre> + * // create a new renderer + * PdfRenderer renderer = new PdfRenderer(getSeekableFileDescriptor()); + * + * // let us just render all pages + * final int pageCount = renderer.getPageCount(); + * for (int i = 0; i < pageCount; i++) { + * Page page = renderer.openPage(i); + * + * // say we render for showing on the screen + * page.render(mBitmap, null, null, Page.RENDER_MODE_FOR_DISPLAY); + * + * // do stuff with the bitmap + * + * // close the page + * page.close(); + * } + * + * // close the renderer + * renderer.close(); + * </pre> + * + * <h3>Print preview and print output</h3> + * <p> + * If you are using this class to rasterize a PDF for printing or show a print + * preview, it is recommended that you respect the following contract in order + * to provide a consistent user experience when seeing a preview and printing, + * i.e. the user sees a preview that is the same as the printout. + * </p> + * <ul> + * <li> + * Respect the property whether the document would like to be scaled for printing + * as per {@link #shouldScaleForPrinting()}. + * </li> + * <li> + * When scaling a document for printing the aspect ratio should be preserved. + * </li> + * <li> + * Do not inset the content with any margins from the {@link android.print.PrintAttributes} + * as the application is responsible to render it such that the margins are respected. + * </li> + * <li> + * If document page size is greater than the printed media size the content should + * be anchored to the upper left corner of the page for left-to-right locales and + * top right corner for right-to-left locales. + * </li> + * </ul> + * + * @see #close() + */ +public final class PdfRenderer implements AutoCloseable { + /** + * Any call the native pdfium code has to be single threaded as the library does not support + * parallel use. + */ + final static Object sPdfiumLock = new Object(); + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + private final Point mTempPoint = new Point(); + + private long mNativeDocument; + + private final int mPageCount; + + private ParcelFileDescriptor mInput; + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + private Page mCurrentPage; + + /** @hide */ + @IntDef({ + Page.RENDER_MODE_FOR_DISPLAY, + Page.RENDER_MODE_FOR_PRINT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RenderMode {} + + /** + * Creates a new instance. + * <p> + * <strong>Note:</strong> The provided file descriptor must be <strong>seekable</strong>, + * i.e. its data being randomly accessed, e.g. pointing to a file. + * </p> + * <p> + * <strong>Note:</strong> This class takes ownership of the passed in file descriptor + * and is responsible for closing it when the renderer is closed. + * </p> + * <p> + * If the file is from an untrusted source it is recommended to run the renderer in a separate, + * isolated process with minimal permissions to limit the impact of security exploits. + * </p> + * + * @param input Seekable file descriptor to read from. + * + * @throws java.io.IOException If an error occurs while reading the file. + * @throws java.lang.SecurityException If the file requires a password or + * the security scheme is not supported. + */ + public PdfRenderer(@NonNull ParcelFileDescriptor input) throws IOException { + if (input == null) { + throw new NullPointerException("input cannot be null"); + } + + final long size; + try { + Os.lseek(input.getFileDescriptor(), 0, OsConstants.SEEK_SET); + size = Os.fstat(input.getFileDescriptor()).st_size; + } catch (ErrnoException ee) { + throw new IllegalArgumentException("file descriptor not seekable"); + } + mInput = input; + + synchronized (sPdfiumLock) { + mNativeDocument = nativeCreate(mInput.getFd(), size); + try { + mPageCount = nativeGetPageCount(mNativeDocument); + } catch (Throwable t) { + nativeClose(mNativeDocument); + mNativeDocument = 0; + throw t; + } + } + + mCloseGuard.open("close"); + } + + /** + * Closes this renderer. You should not use this instance + * after this method is called. + */ + public void close() { + throwIfClosed(); + throwIfPageOpened(); + doClose(); + } + + /** + * Gets the number of pages in the document. + * + * @return The page count. + */ + public int getPageCount() { + throwIfClosed(); + return mPageCount; + } + + /** + * Gets whether the document prefers to be scaled for printing. + * You should take this info account if the document is rendered + * for printing and the target media size differs from the page + * size. + * + * @return If to scale the document. + */ + public boolean shouldScaleForPrinting() { + throwIfClosed(); + + synchronized (sPdfiumLock) { + return nativeScaleForPrinting(mNativeDocument); + } + } + + /** + * Opens a page for rendering. + * + * @param index The page index. + * @return A page that can be rendered. + * + * @see android.graphics.pdf.PdfRenderer.Page#close() PdfRenderer.Page.close() + */ + public Page openPage(int index) { + throwIfClosed(); + throwIfPageOpened(); + throwIfPageNotInDocument(index); + mCurrentPage = new Page(index); + return mCurrentPage; + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + + doClose(); + } finally { + super.finalize(); + } + } + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + private void doClose() { + if (mCurrentPage != null) { + mCurrentPage.close(); + mCurrentPage = null; + } + + if (mNativeDocument != 0) { + synchronized (sPdfiumLock) { + nativeClose(mNativeDocument); + } + mNativeDocument = 0; + } + + if (mInput != null) { + IoUtils.closeQuietly(mInput); + mInput = null; + } + mCloseGuard.close(); + } + + private void throwIfClosed() { + if (mInput == null) { + throw new IllegalStateException("Already closed"); + } + } + + private void throwIfPageOpened() { + if (mCurrentPage != null) { + throw new IllegalStateException("Current page not closed"); + } + } + + private void throwIfPageNotInDocument(int pageIndex) { + if (pageIndex < 0 || pageIndex >= mPageCount) { + throw new IllegalArgumentException("Invalid page index"); + } + } + + /** + * This class represents a PDF document page for rendering. + */ + public final class Page implements AutoCloseable { + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + /** + * Mode to render the content for display on a screen. + */ + public static final int RENDER_MODE_FOR_DISPLAY = 1; + + /** + * Mode to render the content for printing. + */ + public static final int RENDER_MODE_FOR_PRINT = 2; + + private final int mIndex; + private final int mWidth; + private final int mHeight; + + private long mNativePage; + + private Page(int index) { + Point size = mTempPoint; + synchronized (sPdfiumLock) { + mNativePage = nativeOpenPageAndGetSize(mNativeDocument, index, size); + } + mIndex = index; + mWidth = size.x; + mHeight = size.y; + mCloseGuard.open("close"); + } + + /** + * Gets the page index. + * + * @return The index. + */ + public int getIndex() { + return mIndex; + } + + /** + * Gets the page width in points (1/72"). + * + * @return The width in points. + */ + public int getWidth() { + return mWidth; + } + + /** + * Gets the page height in points (1/72"). + * + * @return The height in points. + */ + public int getHeight() { + return mHeight; + } + + /** + * Renders a page to a bitmap. + * <p> + * You may optionally specify a rectangular clip in the bitmap bounds. No rendering + * outside the clip will be performed, hence it is your responsibility to initialize + * the bitmap outside the clip. + * </p> + * <p> + * You may optionally specify a matrix to transform the content from page coordinates + * which are in points (1/72") to bitmap coordinates which are in pixels. If this + * matrix is not provided this method will apply a transformation that will fit the + * whole page to the destination clip if provided or the destination bitmap if no + * clip is provided. + * </p> + * <p> + * The clip and transformation are useful for implementing tile rendering where the + * destination bitmap contains a portion of the image, for example when zooming. + * Another useful application is for printing where the size of the bitmap holding + * the page is too large and a client can render the page in stripes. + * </p> + * <p> + * <strong>Note: </strong> The destination bitmap format must be + * {@link Config#ARGB_8888 ARGB}. + * </p> + * <p> + * <strong>Note: </strong> The optional transformation matrix must be affine as per + * {@link android.graphics.Matrix#isAffine() Matrix.isAffine()}. Hence, you can specify + * rotation, scaling, translation but not a perspective transformation. + * </p> + * + * @param destination Destination bitmap to which to render. + * @param destClip Optional clip in the bitmap bounds. + * @param transform Optional transformation to apply when rendering. + * @param renderMode The render mode. + * + * @see #RENDER_MODE_FOR_DISPLAY + * @see #RENDER_MODE_FOR_PRINT + */ + public void render(@NonNull Bitmap destination, @Nullable Rect destClip, + @Nullable Matrix transform, @RenderMode int renderMode) { + if (mNativePage == 0) { + throw new NullPointerException(); + } + + destination = Preconditions.checkNotNull(destination, "bitmap null"); + + if (destination.getConfig() != Config.ARGB_8888) { + throw new IllegalArgumentException("Unsupported pixel format"); + } + + if (destClip != null) { + if (destClip.left < 0 || destClip.top < 0 + || destClip.right > destination.getWidth() + || destClip.bottom > destination.getHeight()) { + throw new IllegalArgumentException("destBounds not in destination"); + } + } + + if (transform != null && !transform.isAffine()) { + throw new IllegalArgumentException("transform not affine"); + } + + if (renderMode != RENDER_MODE_FOR_PRINT && renderMode != RENDER_MODE_FOR_DISPLAY) { + throw new IllegalArgumentException("Unsupported render mode"); + } + + if (renderMode == RENDER_MODE_FOR_PRINT && renderMode == RENDER_MODE_FOR_DISPLAY) { + throw new IllegalArgumentException("Only single render mode supported"); + } + + final int contentLeft = (destClip != null) ? destClip.left : 0; + final int contentTop = (destClip != null) ? destClip.top : 0; + final int contentRight = (destClip != null) ? destClip.right + : destination.getWidth(); + final int contentBottom = (destClip != null) ? destClip.bottom + : destination.getHeight(); + + // If transform is not set, stretch page to whole clipped area + if (transform == null) { + int clipWidth = contentRight - contentLeft; + int clipHeight = contentBottom - contentTop; + + transform = new Matrix(); + transform.postScale((float)clipWidth / getWidth(), + (float)clipHeight / getHeight()); + transform.postTranslate(contentLeft, contentTop); + } + + // FIXME: This code is planned to be outside the UI rendering module, so it should not + // be able to access native instances from Bitmap, Matrix, etc. + final long transformPtr = transform.ni(); + + synchronized (sPdfiumLock) { + nativeRenderPage(mNativeDocument, mNativePage, destination.getNativeInstance(), + contentLeft, contentTop, contentRight, contentBottom, transformPtr, + renderMode); + } + } + + /** + * Closes this page. + * + * @see android.graphics.pdf.PdfRenderer#openPage(int) + */ + @Override + public void close() { + throwIfClosed(); + doClose(); + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + + doClose(); + } finally { + super.finalize(); + } + } + + private void doClose() { + if (mNativePage != 0) { + synchronized (sPdfiumLock) { + nativeClosePage(mNativePage); + } + mNativePage = 0; + } + + mCloseGuard.close(); + mCurrentPage = null; + } + + private void throwIfClosed() { + if (mNativePage == 0) { + throw new IllegalStateException("Already closed"); + } + } + } + + private static native long nativeCreate(int fd, long size); + private static native void nativeClose(long documentPtr); + private static native int nativeGetPageCount(long documentPtr); + private static native boolean nativeScaleForPrinting(long documentPtr); + private static native void nativeRenderPage(long documentPtr, long pagePtr, long bitmapHandle, + int clipLeft, int clipTop, int clipRight, int clipBottom, long transformPtr, + int renderMode); + private static native long nativeOpenPageAndGetSize(long documentPtr, int pageIndex, + Point outSize); + private static native void nativeClosePage(long pagePtr); +} diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index e4f3e2defb25..4e330da417be 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -427,6 +427,7 @@ cc_defaults { "jni/MovieImpl.cpp", "jni/pdf/PdfDocument.cpp", "jni/pdf/PdfEditor.cpp", + "jni/pdf/PdfRenderer.cpp", "jni/pdf/PdfUtils.cpp", ], shared_libs: [ diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index fb0cdb034575..883f273b5d3d 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -70,6 +70,7 @@ extern int register_android_graphics_fonts_Font(JNIEnv* env); extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); +extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); extern int register_android_graphics_text_MeasuredText(JNIEnv* env); extern int register_android_graphics_text_LineBreaker(JNIEnv *env); extern int register_android_graphics_text_TextShaper(JNIEnv *env); @@ -141,6 +142,7 @@ extern int register_android_graphics_HardwareBufferRenderer(JNIEnv* env); REG_JNI(register_android_graphics_fonts_FontFamily), REG_JNI(register_android_graphics_pdf_PdfDocument), REG_JNI(register_android_graphics_pdf_PdfEditor), + REG_JNI(register_android_graphics_pdf_PdfRenderer), REG_JNI(register_android_graphics_text_MeasuredText), REG_JNI(register_android_graphics_text_LineBreaker), REG_JNI(register_android_graphics_text_TextShaper), diff --git a/libs/hwui/jni/pdf/PdfRenderer.cpp b/libs/hwui/jni/pdf/PdfRenderer.cpp new file mode 100644 index 000000000000..cc1f96197c74 --- /dev/null +++ b/libs/hwui/jni/pdf/PdfRenderer.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PdfUtils.h" + +#include "GraphicsJNI.h" +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "fpdfview.h" + +#include <vector> +#include <utils/Log.h> +#include <unistd.h> +#include <sys/types.h> +#include <unistd.h> + +namespace android { + +static const int RENDER_MODE_FOR_DISPLAY = 1; +static const int RENDER_MODE_FOR_PRINT = 2; + +static struct { + jfieldID x; + jfieldID y; +} gPointClassInfo; + +static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr, + jint pageIndex, jobject outSize) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot load page"); + return -1; + } + + double width = 0; + double height = 0; + + int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return -1; + } + + env->SetIntField(outSize, gPointClassInfo.x, width); + env->SetIntField(outSize, gPointClassInfo.y, height); + + return reinterpret_cast<jlong>(page); +} + +static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) { + FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr); + FPDF_ClosePage(page); +} + +static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr, + jlong bitmapPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom, + jlong transformPtr, jint renderMode) { + FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr); + + SkBitmap skBitmap; + bitmap::toBitmap(bitmapPtr).getSkBitmap(&skBitmap); + + const int stride = skBitmap.width() * 4; + + FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap.width(), skBitmap.height(), + FPDFBitmap_BGRA, skBitmap.getPixels(), stride); + + int renderFlags = FPDF_REVERSE_BYTE_ORDER; + if (renderMode == RENDER_MODE_FOR_DISPLAY) { + renderFlags |= FPDF_LCD_TEXT; + } else if (renderMode == RENDER_MODE_FOR_PRINT) { + renderFlags |= FPDF_PRINTING; + } + + SkMatrix matrix = *reinterpret_cast<SkMatrix*>(transformPtr); + SkScalar transformValues[6]; + if (!matrix.asAffine(transformValues)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "transform matrix has perspective. Only affine matrices are allowed."); + return; + } + + FS_MATRIX transform = {transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], + transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], + transformValues[SkMatrix::kATransX], + transformValues[SkMatrix::kATransY]}; + + FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; + + FPDF_RenderPageBitmapWithMatrix(bitmap, page, &transform, &clip, renderFlags); + + skBitmap.notifyPixelsChanged(); +} + +static const JNINativeMethod gPdfRenderer_Methods[] = { + {"nativeCreate", "(IJ)J", (void*) nativeOpen}, + {"nativeClose", "(J)V", (void*) nativeClose}, + {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, + {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, + {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage}, + {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize}, + {"nativeClosePage", "(J)V", (void*) nativeClosePage} +}; + +int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) { + int result = RegisterMethodsOrDie( + env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods, + NELEM(gPdfRenderer_Methods)); + + jclass clazz = FindClassOrDie(env, "android/graphics/Point"); + gPointClassInfo.x = GetFieldIDOrDie(env, clazz, "x", "I"); + gPointClassInfo.y = GetFieldIDOrDie(env, clazz, "y", "I"); + + return result; +}; + +}; |