Loading core/jni/android/graphics/pdf/PdfEditor.cpp +202 −2 Original line number Original line Diff line number Diff line Loading @@ -19,6 +19,9 @@ #include "fpdfview.h" #include "fpdfview.h" #include "fpdfedit.h" #include "fpdfedit.h" #include "fpdfsave.h" #include "fpdfsave.h" #include "fsdk_rendercontext.h" #include "fpdf_transformpage.h" #include "SkMatrix.h" #include <android_runtime/AndroidRuntime.h> #include <android_runtime/AndroidRuntime.h> #include <vector> #include <vector> Loading @@ -29,6 +32,20 @@ namespace android { namespace android { enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP}; static struct { jfieldID x; jfieldID y; } gPointClassInfo; static struct { jfieldID left; jfieldID top; jfieldID right; jfieldID bottom; } gRectClassInfo; static Mutex sLock; static Mutex sLock; static int sUnmatchedInitRequestCount = 0; static int sUnmatchedInitRequestCount = 0; Loading Loading @@ -144,18 +161,201 @@ static void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) { } } } } static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) { FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); CPDF_Page* page = (CPDF_Page*) FPDF_LoadPage(document, pageIndex); if (!page) { jniThrowException(env, "java/lang/IllegalStateException", "cannot open page"); return; } double width = 0; double height = 0; const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); if (!result) { jniThrowException(env, "java/lang/IllegalStateException", "cannot get page size"); return; } CFX_AffineMatrix matrix; SkMatrix* skTransform = reinterpret_cast<SkMatrix*>(transformPtr); SkScalar transformValues[6]; skTransform->asAffine(transformValues); // PDF's coordinate system origin is left-bottom while in graphics it // is the top-left. So, translate the PDF coordinates to ours. matrix.Set(1, 0, 0, -1, 0, page->GetPageHeight()); // Apply the transformation what was created in our coordinates. matrix.Concat(transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], transformValues[SkMatrix::kATransX], transformValues[SkMatrix::kATransY]); // Translate the result back to PDF coordinates. matrix.Concat(1, 0, 0, -1, 0, page->GetPageHeight()); FS_MATRIX transform = {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f}; FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; FPDFPage_TransFormWithClip(page, &transform, &clip); FPDF_ClosePage(page); } static void nativeGetPageSize(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 open page"); return; } double width = 0; double height = 0; const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); if (!result) { jniThrowException(env, "java/lang/IllegalStateException", "cannot get page size"); return; } env->SetIntField(outSize, gPointClassInfo.x, width); env->SetIntField(outSize, gPointClassInfo.y, height); FPDF_ClosePage(page); } static jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) { FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); FPDF_BOOL success = FPDF_VIEWERREF_GetPrintScaling(document); return success ? JNI_TRUE : JNI_FALSE; } static bool nativeGetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, PageBox pageBox, jobject outBox) { FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); if (!page) { jniThrowException(env, "java/lang/IllegalStateException", "cannot open page"); return false; } float left; float top; float right; float bottom; const FPDF_BOOL success = (pageBox == PAGE_BOX_MEDIA) ? FPDFPage_GetMediaBox(page, &left, &top, &right, &bottom) : FPDFPage_GetCropBox(page, &left, &top, &right, &bottom); FPDF_ClosePage(page); if (!success) { return false; } env->SetIntField(outBox, gRectClassInfo.left, (int) left); env->SetIntField(outBox, gRectClassInfo.top, (int) top); env->SetIntField(outBox, gRectClassInfo.right, (int) right); env->SetIntField(outBox, gRectClassInfo.bottom, (int) bottom); return true; } static jboolean nativeGetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jobject outMediaBox) { const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, outMediaBox); return success ? JNI_TRUE : JNI_FALSE; } static jboolean nativeGetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jobject outMediaBox) { const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, outMediaBox); return success ? JNI_TRUE : JNI_FALSE; } static void nativeSetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, PageBox pageBox, jobject box) { FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); if (!page) { jniThrowException(env, "java/lang/IllegalStateException", "cannot open page"); return; } const int left = env->GetIntField(box, gRectClassInfo.left); const int top = env->GetIntField(box, gRectClassInfo.top); const int right = env->GetIntField(box, gRectClassInfo.right); const int bottom = env->GetIntField(box, gRectClassInfo.bottom); if (pageBox == PAGE_BOX_MEDIA) { FPDFPage_SetMediaBox(page, left, top, right, bottom); } else { FPDFPage_SetCropBox(page, left, top, right, bottom); } FPDF_ClosePage(page); } static void nativeSetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jobject mediaBox) { nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, mediaBox); } static void nativeSetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jobject mediaBox) { nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox); } static JNINativeMethod gPdfEditor_Methods[] = { static JNINativeMethod gPdfEditor_Methods[] = { {"nativeOpen", "(IJ)J", (void*) nativeOpen}, {"nativeOpen", "(IJ)J", (void*) nativeOpen}, {"nativeClose", "(J)V", (void*) nativeClose}, {"nativeClose", "(J)V", (void*) nativeClose}, {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage}, {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage}, {"nativeWrite", "(JI)V", (void*) nativeWrite} {"nativeWrite", "(JI)V", (void*) nativeWrite}, {"nativeSetTransformAndClip", "(JIJIIII)V", (void*) nativeSetTransformAndClip}, {"nativeGetPageSize", "(JILandroid/graphics/Point;)V", (void*) nativeGetPageSize}, {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, {"nativeGetPageMediaBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageMediaBox}, {"nativeSetPageMediaBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageMediaBox}, {"nativeGetPageCropBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageCropBox}, {"nativeSetPageCropBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageCropBox} }; }; int register_android_graphics_pdf_PdfEditor(JNIEnv* env) { int register_android_graphics_pdf_PdfEditor(JNIEnv* env) { return android::AndroidRuntime::registerNativeMethods( const int result = android::AndroidRuntime::registerNativeMethods( env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods, env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods, NELEM(gPdfEditor_Methods)); NELEM(gPdfEditor_Methods)); jclass pointClass = env->FindClass("android/graphics/Point"); gPointClassInfo.x = env->GetFieldID(pointClass, "x", "I"); gPointClassInfo.y = env->GetFieldID(pointClass, "y", "I"); jclass rectClass = env->FindClass("android/graphics/Rect"); gRectClassInfo.left = env->GetFieldID(rectClass, "left", "I"); gRectClassInfo.top = env->GetFieldID(rectClass, "top", "I"); gRectClassInfo.right = env->GetFieldID(rectClass, "right", "I"); gRectClassInfo.bottom = env->GetFieldID(rectClass, "bottom", "I"); return result; }; }; }; }; graphics/java/android/graphics/pdf/PdfEditor.java +155 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,10 @@ package android.graphics.pdf; package android.graphics.pdf; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.ErrnoException; import android.system.OsConstants; import android.system.OsConstants; Loading Loading @@ -97,6 +101,109 @@ public final class PdfEditor { mPageCount = nativeRemovePage(mNativeDocument, pageIndex); mPageCount = nativeRemovePage(mNativeDocument, pageIndex); } } /** * Sets a transformation and clip for a given page. The transformation matrix if * non-null must be affine as per {@link android.graphics.Matrix#isAffine()}. If * the clip is null, then no clipping is performed. * * @param pageIndex The page whose transform to set. * @param transform The transformation to apply. * @param clip The clip to apply. */ public void setTransformAndClip(int pageIndex, @Nullable Matrix transform, @Nullable Rect clip) { throwIfClosed(); throwIfPageNotInDocument(pageIndex); throwIfNotNullAndNotAfine(transform); if (transform == null) { transform = Matrix.IDENTITY_MATRIX; } if (clip == null) { Point size = new Point(); getPageSize(pageIndex, size); nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance, 0, 0, size.x, size.y); } else { nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance, clip.left, clip.top, clip.right, clip.bottom); } } /** * Gets the size of a given page in mils (1/72"). * * @param pageIndex The page index. * @param outSize The size output. */ public void getPageSize(int pageIndex, @NonNull Point outSize) { throwIfClosed(); throwIfOutSizeNull(outSize); throwIfPageNotInDocument(pageIndex); nativeGetPageSize(mNativeDocument, pageIndex, outSize); } /** * Gets the media box of a given page in mils (1/72"). * * @param pageIndex The page index. * @param outMediaBox The media box output. */ public boolean getPageMediaBox(int pageIndex, @NonNull Rect outMediaBox) { throwIfClosed(); throwIfOutMediaBoxNull(outMediaBox); throwIfPageNotInDocument(pageIndex); return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox); } /** * Sets the media box of a given page in mils (1/72"). * * @param pageIndex The page index. * @param mediaBox The media box. */ public void setPageMediaBox(int pageIndex, @NonNull Rect mediaBox) { throwIfClosed(); throwIfMediaBoxNull(mediaBox); throwIfPageNotInDocument(pageIndex); nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox); } /** * Gets the crop box of a given page in mils (1/72"). * * @param pageIndex The page index. * @param outCropBox The crop box output. */ public boolean getPageCropBox(int pageIndex, @NonNull Rect outCropBox) { throwIfClosed(); throwIfOutCropBoxNull(outCropBox); throwIfPageNotInDocument(pageIndex); return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox); } /** * Sets the crop box of a given page in mils (1/72"). * * @param pageIndex The page index. * @param cropBox The crop box. */ public void setPageCropBox(int pageIndex, @NonNull Rect cropBox) { throwIfClosed(); throwIfCropBoxNull(cropBox); throwIfPageNotInDocument(pageIndex); nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox); } /** * Gets whether the document prefers to be scaled for printing. * * @return Whether to scale the document. */ public boolean shouldScaleForPrinting() { throwIfClosed(); return nativeScaleForPrinting(mNativeDocument); } /** /** * Writes the PDF file to the provided destination. * Writes the PDF file to the provided destination. * <p> * <p> Loading Loading @@ -154,9 +261,57 @@ public final class PdfEditor { } } } } private void throwIfNotNullAndNotAfine(Matrix matrix) { if (matrix != null && !matrix.isAffine()) { throw new IllegalStateException("Matrix must be afine"); } } private void throwIfOutSizeNull(Point outSize) { if (outSize == null) { throw new NullPointerException("outSize cannot be null"); } } private void throwIfOutMediaBoxNull(Rect outMediaBox) { if (outMediaBox == null) { throw new NullPointerException("outMediaBox cannot be null"); } } private void throwIfMediaBoxNull(Rect mediaBox) { if (mediaBox == null) { throw new NullPointerException("mediaBox cannot be null"); } } private void throwIfOutCropBoxNull(Rect outCropBox) { if (outCropBox == null) { throw new NullPointerException("outCropBox cannot be null"); } } private void throwIfCropBoxNull(Rect cropBox) { if (cropBox == null) { throw new NullPointerException("cropBox cannot be null"); } } private static native long nativeOpen(int fd, long size); private static native long nativeOpen(int fd, long size); private static native void nativeClose(long documentPtr); private static native void nativeClose(long documentPtr); private static native int nativeGetPageCount(long documentPtr); private static native int nativeGetPageCount(long documentPtr); private static native int nativeRemovePage(long documentPtr, int pageIndex); private static native int nativeRemovePage(long documentPtr, int pageIndex); private static native void nativeWrite(long documentPtr, int fd); private static native void nativeWrite(long documentPtr, int fd); private static native void nativeSetTransformAndClip(long documentPtr, int pageIndex, long transformPtr, int clipLeft, int clipTop, int clipRight, int clipBottom); private static native void nativeGetPageSize(long documentPtr, int pageIndex, Point outSize); private static native boolean nativeGetPageMediaBox(long documentPtr, int pageIndex, Rect outMediaBox); private static native void nativeSetPageMediaBox(long documentPtr, int pageIndex, Rect mediaBox); private static native boolean nativeGetPageCropBox(long documentPtr, int pageIndex, Rect outMediaBox); private static native void nativeSetPageCropBox(long documentPtr, int pageIndex, Rect mediaBox); private static native boolean nativeScaleForPrinting(long documentPtr); } } packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl +2 −0 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.printspooler.renderer; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor; import android.print.PageRange; import android.print.PageRange; import android.print.PrintAttributes; /** /** * Interface for communication with a remote pdf editor. * Interface for communication with a remote pdf editor. Loading @@ -25,6 +26,7 @@ import android.print.PageRange; interface IPdfEditor { interface IPdfEditor { int openDocument(in ParcelFileDescriptor source); int openDocument(in ParcelFileDescriptor source); void removePages(in PageRange[] pages); void removePages(in PageRange[] pages); void applyPrintAttributes(in PrintAttributes attributes); void write(in ParcelFileDescriptor destination); void write(in ParcelFileDescriptor destination); void closeDocument(); void closeDocument(); } } packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java +107 −2 Original line number Original line Diff line number Diff line Loading @@ -245,6 +245,111 @@ public final class PdfManipulationService extends Service { } } } } @Override public void applyPrintAttributes(PrintAttributes attributes) { synchronized (mLock) { throwIfNotOpened(); if (DEBUG) { Log.i(LOG_TAG, "applyPrintAttributes()"); } Rect mediaBox = new Rect(); Rect cropBox = new Rect(); Matrix transform = new Matrix(); final boolean contentPortrait = attributes.getMediaSize().isPortrait(); final boolean layoutDirectionRtl = getResources().getConfiguration() .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; // We do not want to rotate the media box, so take into account orientation. final int dstWidthPts = contentPortrait ? pointsFromMils(attributes.getMediaSize().getWidthMils()) : pointsFromMils(attributes.getMediaSize().getHeightMils()); final int dstHeightPts = contentPortrait ? pointsFromMils(attributes.getMediaSize().getHeightMils()) : pointsFromMils(attributes.getMediaSize().getWidthMils()); final boolean scaleForPrinting = mEditor.shouldScaleForPrinting(); final int pageCount = mEditor.getPageCount(); for (int i = 0; i < pageCount; i++) { if (!mEditor.getPageMediaBox(i, mediaBox)) { Log.e(LOG_TAG, "Malformed PDF file"); return; } final int srcWidthPts = mediaBox.width(); final int srcHeightPts = mediaBox.height(); // Update the media box with the desired size. mediaBox.right = dstWidthPts; mediaBox.bottom = dstHeightPts; mEditor.setPageMediaBox(i, mediaBox); // Make sure content is top-left after media box resize. transform.setTranslate(0, srcHeightPts - dstHeightPts); // Rotate the content if in landscape. if (!contentPortrait) { transform.postRotate(270); transform.postTranslate(0, dstHeightPts); } // Scale the content if document allows it. final float scale; if (scaleForPrinting) { if (contentPortrait) { scale = Math.min((float) dstWidthPts / srcWidthPts, (float) dstHeightPts / srcHeightPts); transform.postScale(scale, scale); } else { scale = Math.min((float) dstWidthPts / srcHeightPts, (float) dstHeightPts / srcWidthPts); transform.postScale(scale, scale, mediaBox.left, mediaBox.bottom); } } else { scale = 1.0f; } // Update the crop box relatively to the media box change, if needed. if (mEditor.getPageCropBox(i, cropBox)) { cropBox.left = (int) (cropBox.left * scale + 0.5f); cropBox.top = (int) (cropBox.top * scale + 0.5f); cropBox.right = (int) (cropBox.right * scale + 0.5f); cropBox.bottom = (int) (cropBox.bottom * scale + 0.5f); cropBox.intersect(mediaBox); mEditor.setPageCropBox(i, cropBox); } // If in RTL mode put the content in the logical top-right corner. if (layoutDirectionRtl) { final float dx = contentPortrait ? dstWidthPts - (int) (srcWidthPts * scale + 0.5f) : 0; final float dy = contentPortrait ? 0 : - (dstHeightPts - (int) (srcWidthPts * scale + 0.5f)); transform.postTranslate(dx, dy); } // Adjust the physical margins if needed. Margins minMargins = attributes.getMinMargins(); final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils()); final int paddingTopPts = pointsFromMils(minMargins.getTopMils()); final int paddingRightPts = pointsFromMils(minMargins.getRightMils()); final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils()); Rect clip = new Rect(mediaBox); clip.left += paddingLeftPts; clip.top += paddingTopPts; clip.right -= paddingRightPts; clip.bottom -= paddingBottomPts; // Apply the accumulated transforms. mEditor.setTransformAndClip(i, transform, clip); } } } @Override @Override public void write(ParcelFileDescriptor destination) throws RemoteException { public void write(ParcelFileDescriptor destination) throws RemoteException { synchronized (mLock) { synchronized (mLock) { Loading packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +25 −13 Original line number Original line Diff line number Diff line Loading @@ -592,7 +592,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mDestinationSpinner.post(new Runnable() { mDestinationSpinner.post(new Runnable() { @Override @Override public void run() { public void run() { shredPagesAndFinish(uri); transformDocumentAndFinish(uri); } } }); }); } else if (resultCode == RESULT_CANCELED) { } else if (resultCode == RESULT_CANCELED) { Loading Loading @@ -922,7 +922,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) { if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) { startCreateDocumentActivity(); startCreateDocumentActivity(); } else { } else { shredPagesAndFinish(null); transformDocumentAndFinish(null); } } } } Loading Loading @@ -1597,8 +1597,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat return true; return true; } } private void shredPagesAndFinish(final Uri writeToUri) { private void transformDocumentAndFinish(final Uri writeToUri) { new PageShredder(this, mPrintJob, mFileProvider, new Runnable() { // If saving to PDF, apply the attibutes as we are acting as a print service. PrintAttributes attributes = mDestinationSpinnerAdapter.getPdfPrinter() == mCurrentPrinter ? mPrintJob.getAttributes() : null; new DocumentTransformer(this, mPrintJob, mFileProvider, attributes, new Runnable() { @Override @Override public void run() { public void run() { if (writeToUri != null) { if (writeToUri != null) { Loading @@ -1606,7 +1609,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } } doFinish(); doFinish(); } } }).shred(); }).transform(); } } private void doFinish() { private void doFinish() { Loading Loading @@ -2329,7 +2332,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } } } } private static final class PageShredder implements ServiceConnection { private static final class DocumentTransformer implements ServiceConnection { private static final String TEMP_FILE_PREFIX = "print_job"; private static final String TEMP_FILE_PREFIX = "print_job"; private static final String TEMP_FILE_EXTENSION = ".pdf"; private static final String TEMP_FILE_EXTENSION = ".pdf"; Loading @@ -2341,20 +2344,24 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private final PageRange[] mPagesToShred; private final PageRange[] mPagesToShred; private final PrintAttributes mAttributesToApply; private final Runnable mCallback; private final Runnable mCallback; public PageShredder(Context context, PrintJobInfo printJob, public DocumentTransformer(Context context, PrintJobInfo printJob, MutexFileProvider fileProvider, Runnable callback) { MutexFileProvider fileProvider, PrintAttributes attributes, Runnable callback) { mContext = context; mContext = context; mPrintJob = printJob; mPrintJob = printJob; mFileProvider = fileProvider; mFileProvider = fileProvider; mCallback = callback; mCallback = callback; mPagesToShred = computePagesToShred(mPrintJob); mPagesToShred = computePagesToShred(mPrintJob); mAttributesToApply = attributes; } } public void shred() { public void transform() { // If we have only the pages we want, done. // If we have only the pages we want, done. if (mPagesToShred.length <= 0) { if (mPagesToShred.length <= 0 && mAttributesToApply == null) { mCallback.run(); mCallback.run(); return; return; } } Loading @@ -2376,14 +2383,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // final and this code is the last one to touch // final and this code is the last one to touch // them as shredding is the very last step, so the // them as shredding is the very last step, so the // UI is not interactive at this point. // UI is not interactive at this point. shredPages(editor); doTransform(editor); updatePrintJob(); updatePrintJob(); return null; return null; } } @Override @Override protected void onPostExecute(Void aVoid) { protected void onPostExecute(Void aVoid) { mContext.unbindService(PageShredder.this); mContext.unbindService(DocumentTransformer.this); mCallback.run(); mCallback.run(); } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); Loading @@ -2394,7 +2401,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat /* do nothing */ /* do nothing */ } } private void shredPages(IPdfEditor editor) { private void doTransform(IPdfEditor editor) { File tempFile = null; File tempFile = null; ParcelFileDescriptor src = null; ParcelFileDescriptor src = null; ParcelFileDescriptor dst = null; ParcelFileDescriptor dst = null; Loading @@ -2413,6 +2420,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // Drop the pages. // Drop the pages. editor.removePages(mPagesToShred); editor.removePages(mPagesToShred); // Apply print attributes if needed. if (mAttributesToApply != null) { editor.applyPrintAttributes(mAttributesToApply); } // Write the modified PDF to a temp file. // Write the modified PDF to a temp file. tempFile = File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_EXTENSION, tempFile = File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_EXTENSION, mContext.getCacheDir()); mContext.getCacheDir()); Loading Loading
core/jni/android/graphics/pdf/PdfEditor.cpp +202 −2 Original line number Original line Diff line number Diff line Loading @@ -19,6 +19,9 @@ #include "fpdfview.h" #include "fpdfview.h" #include "fpdfedit.h" #include "fpdfedit.h" #include "fpdfsave.h" #include "fpdfsave.h" #include "fsdk_rendercontext.h" #include "fpdf_transformpage.h" #include "SkMatrix.h" #include <android_runtime/AndroidRuntime.h> #include <android_runtime/AndroidRuntime.h> #include <vector> #include <vector> Loading @@ -29,6 +32,20 @@ namespace android { namespace android { enum PageBox {PAGE_BOX_MEDIA, PAGE_BOX_CROP}; static struct { jfieldID x; jfieldID y; } gPointClassInfo; static struct { jfieldID left; jfieldID top; jfieldID right; jfieldID bottom; } gRectClassInfo; static Mutex sLock; static Mutex sLock; static int sUnmatchedInitRequestCount = 0; static int sUnmatchedInitRequestCount = 0; Loading Loading @@ -144,18 +161,201 @@ static void nativeWrite(JNIEnv* env, jclass thiz, jlong documentPtr, jint fd) { } } } } static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) { FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); CPDF_Page* page = (CPDF_Page*) FPDF_LoadPage(document, pageIndex); if (!page) { jniThrowException(env, "java/lang/IllegalStateException", "cannot open page"); return; } double width = 0; double height = 0; const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); if (!result) { jniThrowException(env, "java/lang/IllegalStateException", "cannot get page size"); return; } CFX_AffineMatrix matrix; SkMatrix* skTransform = reinterpret_cast<SkMatrix*>(transformPtr); SkScalar transformValues[6]; skTransform->asAffine(transformValues); // PDF's coordinate system origin is left-bottom while in graphics it // is the top-left. So, translate the PDF coordinates to ours. matrix.Set(1, 0, 0, -1, 0, page->GetPageHeight()); // Apply the transformation what was created in our coordinates. matrix.Concat(transformValues[SkMatrix::kAScaleX], transformValues[SkMatrix::kASkewY], transformValues[SkMatrix::kASkewX], transformValues[SkMatrix::kAScaleY], transformValues[SkMatrix::kATransX], transformValues[SkMatrix::kATransY]); // Translate the result back to PDF coordinates. matrix.Concat(1, 0, 0, -1, 0, page->GetPageHeight()); FS_MATRIX transform = {matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f}; FS_RECTF clip = {(float) clipLeft, (float) clipTop, (float) clipRight, (float) clipBottom}; FPDFPage_TransFormWithClip(page, &transform, &clip); FPDF_ClosePage(page); } static void nativeGetPageSize(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 open page"); return; } double width = 0; double height = 0; const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); if (!result) { jniThrowException(env, "java/lang/IllegalStateException", "cannot get page size"); return; } env->SetIntField(outSize, gPointClassInfo.x, width); env->SetIntField(outSize, gPointClassInfo.y, height); FPDF_ClosePage(page); } static jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) { FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); FPDF_BOOL success = FPDF_VIEWERREF_GetPrintScaling(document); return success ? JNI_TRUE : JNI_FALSE; } static bool nativeGetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, PageBox pageBox, jobject outBox) { FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); if (!page) { jniThrowException(env, "java/lang/IllegalStateException", "cannot open page"); return false; } float left; float top; float right; float bottom; const FPDF_BOOL success = (pageBox == PAGE_BOX_MEDIA) ? FPDFPage_GetMediaBox(page, &left, &top, &right, &bottom) : FPDFPage_GetCropBox(page, &left, &top, &right, &bottom); FPDF_ClosePage(page); if (!success) { return false; } env->SetIntField(outBox, gRectClassInfo.left, (int) left); env->SetIntField(outBox, gRectClassInfo.top, (int) top); env->SetIntField(outBox, gRectClassInfo.right, (int) right); env->SetIntField(outBox, gRectClassInfo.bottom, (int) bottom); return true; } static jboolean nativeGetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jobject outMediaBox) { const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, outMediaBox); return success ? JNI_TRUE : JNI_FALSE; } static jboolean nativeGetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jobject outMediaBox) { const bool success = nativeGetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, outMediaBox); return success ? JNI_TRUE : JNI_FALSE; } static void nativeSetPageBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, PageBox pageBox, jobject box) { FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); if (!page) { jniThrowException(env, "java/lang/IllegalStateException", "cannot open page"); return; } const int left = env->GetIntField(box, gRectClassInfo.left); const int top = env->GetIntField(box, gRectClassInfo.top); const int right = env->GetIntField(box, gRectClassInfo.right); const int bottom = env->GetIntField(box, gRectClassInfo.bottom); if (pageBox == PAGE_BOX_MEDIA) { FPDFPage_SetMediaBox(page, left, top, right, bottom); } else { FPDFPage_SetCropBox(page, left, top, right, bottom); } FPDF_ClosePage(page); } static void nativeSetPageMediaBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jobject mediaBox) { nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_MEDIA, mediaBox); } static void nativeSetPageCropBox(JNIEnv* env, jclass thiz, jlong documentPtr, jint pageIndex, jobject mediaBox) { nativeSetPageBox(env, thiz, documentPtr, pageIndex, PAGE_BOX_CROP, mediaBox); } static JNINativeMethod gPdfEditor_Methods[] = { static JNINativeMethod gPdfEditor_Methods[] = { {"nativeOpen", "(IJ)J", (void*) nativeOpen}, {"nativeOpen", "(IJ)J", (void*) nativeOpen}, {"nativeClose", "(J)V", (void*) nativeClose}, {"nativeClose", "(J)V", (void*) nativeClose}, {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage}, {"nativeRemovePage", "(JI)I", (void*) nativeRemovePage}, {"nativeWrite", "(JI)V", (void*) nativeWrite} {"nativeWrite", "(JI)V", (void*) nativeWrite}, {"nativeSetTransformAndClip", "(JIJIIII)V", (void*) nativeSetTransformAndClip}, {"nativeGetPageSize", "(JILandroid/graphics/Point;)V", (void*) nativeGetPageSize}, {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, {"nativeGetPageMediaBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageMediaBox}, {"nativeSetPageMediaBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageMediaBox}, {"nativeGetPageCropBox", "(JILandroid/graphics/Rect;)Z", (void*) nativeGetPageCropBox}, {"nativeSetPageCropBox", "(JILandroid/graphics/Rect;)V", (void*) nativeSetPageCropBox} }; }; int register_android_graphics_pdf_PdfEditor(JNIEnv* env) { int register_android_graphics_pdf_PdfEditor(JNIEnv* env) { return android::AndroidRuntime::registerNativeMethods( const int result = android::AndroidRuntime::registerNativeMethods( env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods, env, "android/graphics/pdf/PdfEditor", gPdfEditor_Methods, NELEM(gPdfEditor_Methods)); NELEM(gPdfEditor_Methods)); jclass pointClass = env->FindClass("android/graphics/Point"); gPointClassInfo.x = env->GetFieldID(pointClass, "x", "I"); gPointClassInfo.y = env->GetFieldID(pointClass, "y", "I"); jclass rectClass = env->FindClass("android/graphics/Rect"); gRectClassInfo.left = env->GetFieldID(rectClass, "left", "I"); gRectClassInfo.top = env->GetFieldID(rectClass, "top", "I"); gRectClassInfo.right = env->GetFieldID(rectClass, "right", "I"); gRectClassInfo.bottom = env->GetFieldID(rectClass, "bottom", "I"); return result; }; }; }; };
graphics/java/android/graphics/pdf/PdfEditor.java +155 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,10 @@ package android.graphics.pdf; package android.graphics.pdf; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.ErrnoException; import android.system.OsConstants; import android.system.OsConstants; Loading Loading @@ -97,6 +101,109 @@ public final class PdfEditor { mPageCount = nativeRemovePage(mNativeDocument, pageIndex); mPageCount = nativeRemovePage(mNativeDocument, pageIndex); } } /** * Sets a transformation and clip for a given page. The transformation matrix if * non-null must be affine as per {@link android.graphics.Matrix#isAffine()}. If * the clip is null, then no clipping is performed. * * @param pageIndex The page whose transform to set. * @param transform The transformation to apply. * @param clip The clip to apply. */ public void setTransformAndClip(int pageIndex, @Nullable Matrix transform, @Nullable Rect clip) { throwIfClosed(); throwIfPageNotInDocument(pageIndex); throwIfNotNullAndNotAfine(transform); if (transform == null) { transform = Matrix.IDENTITY_MATRIX; } if (clip == null) { Point size = new Point(); getPageSize(pageIndex, size); nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance, 0, 0, size.x, size.y); } else { nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance, clip.left, clip.top, clip.right, clip.bottom); } } /** * Gets the size of a given page in mils (1/72"). * * @param pageIndex The page index. * @param outSize The size output. */ public void getPageSize(int pageIndex, @NonNull Point outSize) { throwIfClosed(); throwIfOutSizeNull(outSize); throwIfPageNotInDocument(pageIndex); nativeGetPageSize(mNativeDocument, pageIndex, outSize); } /** * Gets the media box of a given page in mils (1/72"). * * @param pageIndex The page index. * @param outMediaBox The media box output. */ public boolean getPageMediaBox(int pageIndex, @NonNull Rect outMediaBox) { throwIfClosed(); throwIfOutMediaBoxNull(outMediaBox); throwIfPageNotInDocument(pageIndex); return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox); } /** * Sets the media box of a given page in mils (1/72"). * * @param pageIndex The page index. * @param mediaBox The media box. */ public void setPageMediaBox(int pageIndex, @NonNull Rect mediaBox) { throwIfClosed(); throwIfMediaBoxNull(mediaBox); throwIfPageNotInDocument(pageIndex); nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox); } /** * Gets the crop box of a given page in mils (1/72"). * * @param pageIndex The page index. * @param outCropBox The crop box output. */ public boolean getPageCropBox(int pageIndex, @NonNull Rect outCropBox) { throwIfClosed(); throwIfOutCropBoxNull(outCropBox); throwIfPageNotInDocument(pageIndex); return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox); } /** * Sets the crop box of a given page in mils (1/72"). * * @param pageIndex The page index. * @param cropBox The crop box. */ public void setPageCropBox(int pageIndex, @NonNull Rect cropBox) { throwIfClosed(); throwIfCropBoxNull(cropBox); throwIfPageNotInDocument(pageIndex); nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox); } /** * Gets whether the document prefers to be scaled for printing. * * @return Whether to scale the document. */ public boolean shouldScaleForPrinting() { throwIfClosed(); return nativeScaleForPrinting(mNativeDocument); } /** /** * Writes the PDF file to the provided destination. * Writes the PDF file to the provided destination. * <p> * <p> Loading Loading @@ -154,9 +261,57 @@ public final class PdfEditor { } } } } private void throwIfNotNullAndNotAfine(Matrix matrix) { if (matrix != null && !matrix.isAffine()) { throw new IllegalStateException("Matrix must be afine"); } } private void throwIfOutSizeNull(Point outSize) { if (outSize == null) { throw new NullPointerException("outSize cannot be null"); } } private void throwIfOutMediaBoxNull(Rect outMediaBox) { if (outMediaBox == null) { throw new NullPointerException("outMediaBox cannot be null"); } } private void throwIfMediaBoxNull(Rect mediaBox) { if (mediaBox == null) { throw new NullPointerException("mediaBox cannot be null"); } } private void throwIfOutCropBoxNull(Rect outCropBox) { if (outCropBox == null) { throw new NullPointerException("outCropBox cannot be null"); } } private void throwIfCropBoxNull(Rect cropBox) { if (cropBox == null) { throw new NullPointerException("cropBox cannot be null"); } } private static native long nativeOpen(int fd, long size); private static native long nativeOpen(int fd, long size); private static native void nativeClose(long documentPtr); private static native void nativeClose(long documentPtr); private static native int nativeGetPageCount(long documentPtr); private static native int nativeGetPageCount(long documentPtr); private static native int nativeRemovePage(long documentPtr, int pageIndex); private static native int nativeRemovePage(long documentPtr, int pageIndex); private static native void nativeWrite(long documentPtr, int fd); private static native void nativeWrite(long documentPtr, int fd); private static native void nativeSetTransformAndClip(long documentPtr, int pageIndex, long transformPtr, int clipLeft, int clipTop, int clipRight, int clipBottom); private static native void nativeGetPageSize(long documentPtr, int pageIndex, Point outSize); private static native boolean nativeGetPageMediaBox(long documentPtr, int pageIndex, Rect outMediaBox); private static native void nativeSetPageMediaBox(long documentPtr, int pageIndex, Rect mediaBox); private static native boolean nativeGetPageCropBox(long documentPtr, int pageIndex, Rect outMediaBox); private static native void nativeSetPageCropBox(long documentPtr, int pageIndex, Rect mediaBox); private static native boolean nativeScaleForPrinting(long documentPtr); } }
packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl +2 −0 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.printspooler.renderer; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor; import android.print.PageRange; import android.print.PageRange; import android.print.PrintAttributes; /** /** * Interface for communication with a remote pdf editor. * Interface for communication with a remote pdf editor. Loading @@ -25,6 +26,7 @@ import android.print.PageRange; interface IPdfEditor { interface IPdfEditor { int openDocument(in ParcelFileDescriptor source); int openDocument(in ParcelFileDescriptor source); void removePages(in PageRange[] pages); void removePages(in PageRange[] pages); void applyPrintAttributes(in PrintAttributes attributes); void write(in ParcelFileDescriptor destination); void write(in ParcelFileDescriptor destination); void closeDocument(); void closeDocument(); } }
packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java +107 −2 Original line number Original line Diff line number Diff line Loading @@ -245,6 +245,111 @@ public final class PdfManipulationService extends Service { } } } } @Override public void applyPrintAttributes(PrintAttributes attributes) { synchronized (mLock) { throwIfNotOpened(); if (DEBUG) { Log.i(LOG_TAG, "applyPrintAttributes()"); } Rect mediaBox = new Rect(); Rect cropBox = new Rect(); Matrix transform = new Matrix(); final boolean contentPortrait = attributes.getMediaSize().isPortrait(); final boolean layoutDirectionRtl = getResources().getConfiguration() .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; // We do not want to rotate the media box, so take into account orientation. final int dstWidthPts = contentPortrait ? pointsFromMils(attributes.getMediaSize().getWidthMils()) : pointsFromMils(attributes.getMediaSize().getHeightMils()); final int dstHeightPts = contentPortrait ? pointsFromMils(attributes.getMediaSize().getHeightMils()) : pointsFromMils(attributes.getMediaSize().getWidthMils()); final boolean scaleForPrinting = mEditor.shouldScaleForPrinting(); final int pageCount = mEditor.getPageCount(); for (int i = 0; i < pageCount; i++) { if (!mEditor.getPageMediaBox(i, mediaBox)) { Log.e(LOG_TAG, "Malformed PDF file"); return; } final int srcWidthPts = mediaBox.width(); final int srcHeightPts = mediaBox.height(); // Update the media box with the desired size. mediaBox.right = dstWidthPts; mediaBox.bottom = dstHeightPts; mEditor.setPageMediaBox(i, mediaBox); // Make sure content is top-left after media box resize. transform.setTranslate(0, srcHeightPts - dstHeightPts); // Rotate the content if in landscape. if (!contentPortrait) { transform.postRotate(270); transform.postTranslate(0, dstHeightPts); } // Scale the content if document allows it. final float scale; if (scaleForPrinting) { if (contentPortrait) { scale = Math.min((float) dstWidthPts / srcWidthPts, (float) dstHeightPts / srcHeightPts); transform.postScale(scale, scale); } else { scale = Math.min((float) dstWidthPts / srcHeightPts, (float) dstHeightPts / srcWidthPts); transform.postScale(scale, scale, mediaBox.left, mediaBox.bottom); } } else { scale = 1.0f; } // Update the crop box relatively to the media box change, if needed. if (mEditor.getPageCropBox(i, cropBox)) { cropBox.left = (int) (cropBox.left * scale + 0.5f); cropBox.top = (int) (cropBox.top * scale + 0.5f); cropBox.right = (int) (cropBox.right * scale + 0.5f); cropBox.bottom = (int) (cropBox.bottom * scale + 0.5f); cropBox.intersect(mediaBox); mEditor.setPageCropBox(i, cropBox); } // If in RTL mode put the content in the logical top-right corner. if (layoutDirectionRtl) { final float dx = contentPortrait ? dstWidthPts - (int) (srcWidthPts * scale + 0.5f) : 0; final float dy = contentPortrait ? 0 : - (dstHeightPts - (int) (srcWidthPts * scale + 0.5f)); transform.postTranslate(dx, dy); } // Adjust the physical margins if needed. Margins minMargins = attributes.getMinMargins(); final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils()); final int paddingTopPts = pointsFromMils(minMargins.getTopMils()); final int paddingRightPts = pointsFromMils(minMargins.getRightMils()); final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils()); Rect clip = new Rect(mediaBox); clip.left += paddingLeftPts; clip.top += paddingTopPts; clip.right -= paddingRightPts; clip.bottom -= paddingBottomPts; // Apply the accumulated transforms. mEditor.setTransformAndClip(i, transform, clip); } } } @Override @Override public void write(ParcelFileDescriptor destination) throws RemoteException { public void write(ParcelFileDescriptor destination) throws RemoteException { synchronized (mLock) { synchronized (mLock) { Loading
packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +25 −13 Original line number Original line Diff line number Diff line Loading @@ -592,7 +592,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mDestinationSpinner.post(new Runnable() { mDestinationSpinner.post(new Runnable() { @Override @Override public void run() { public void run() { shredPagesAndFinish(uri); transformDocumentAndFinish(uri); } } }); }); } else if (resultCode == RESULT_CANCELED) { } else if (resultCode == RESULT_CANCELED) { Loading Loading @@ -922,7 +922,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) { if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) { startCreateDocumentActivity(); startCreateDocumentActivity(); } else { } else { shredPagesAndFinish(null); transformDocumentAndFinish(null); } } } } Loading Loading @@ -1597,8 +1597,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat return true; return true; } } private void shredPagesAndFinish(final Uri writeToUri) { private void transformDocumentAndFinish(final Uri writeToUri) { new PageShredder(this, mPrintJob, mFileProvider, new Runnable() { // If saving to PDF, apply the attibutes as we are acting as a print service. PrintAttributes attributes = mDestinationSpinnerAdapter.getPdfPrinter() == mCurrentPrinter ? mPrintJob.getAttributes() : null; new DocumentTransformer(this, mPrintJob, mFileProvider, attributes, new Runnable() { @Override @Override public void run() { public void run() { if (writeToUri != null) { if (writeToUri != null) { Loading @@ -1606,7 +1609,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } } doFinish(); doFinish(); } } }).shred(); }).transform(); } } private void doFinish() { private void doFinish() { Loading Loading @@ -2329,7 +2332,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } } } } private static final class PageShredder implements ServiceConnection { private static final class DocumentTransformer implements ServiceConnection { private static final String TEMP_FILE_PREFIX = "print_job"; private static final String TEMP_FILE_PREFIX = "print_job"; private static final String TEMP_FILE_EXTENSION = ".pdf"; private static final String TEMP_FILE_EXTENSION = ".pdf"; Loading @@ -2341,20 +2344,24 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private final PageRange[] mPagesToShred; private final PageRange[] mPagesToShred; private final PrintAttributes mAttributesToApply; private final Runnable mCallback; private final Runnable mCallback; public PageShredder(Context context, PrintJobInfo printJob, public DocumentTransformer(Context context, PrintJobInfo printJob, MutexFileProvider fileProvider, Runnable callback) { MutexFileProvider fileProvider, PrintAttributes attributes, Runnable callback) { mContext = context; mContext = context; mPrintJob = printJob; mPrintJob = printJob; mFileProvider = fileProvider; mFileProvider = fileProvider; mCallback = callback; mCallback = callback; mPagesToShred = computePagesToShred(mPrintJob); mPagesToShred = computePagesToShred(mPrintJob); mAttributesToApply = attributes; } } public void shred() { public void transform() { // If we have only the pages we want, done. // If we have only the pages we want, done. if (mPagesToShred.length <= 0) { if (mPagesToShred.length <= 0 && mAttributesToApply == null) { mCallback.run(); mCallback.run(); return; return; } } Loading @@ -2376,14 +2383,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // final and this code is the last one to touch // final and this code is the last one to touch // them as shredding is the very last step, so the // them as shredding is the very last step, so the // UI is not interactive at this point. // UI is not interactive at this point. shredPages(editor); doTransform(editor); updatePrintJob(); updatePrintJob(); return null; return null; } } @Override @Override protected void onPostExecute(Void aVoid) { protected void onPostExecute(Void aVoid) { mContext.unbindService(PageShredder.this); mContext.unbindService(DocumentTransformer.this); mCallback.run(); mCallback.run(); } } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); Loading @@ -2394,7 +2401,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat /* do nothing */ /* do nothing */ } } private void shredPages(IPdfEditor editor) { private void doTransform(IPdfEditor editor) { File tempFile = null; File tempFile = null; ParcelFileDescriptor src = null; ParcelFileDescriptor src = null; ParcelFileDescriptor dst = null; ParcelFileDescriptor dst = null; Loading @@ -2413,6 +2420,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // Drop the pages. // Drop the pages. editor.removePages(mPagesToShred); editor.removePages(mPagesToShred); // Apply print attributes if needed. if (mAttributesToApply != null) { editor.applyPrintAttributes(mAttributesToApply); } // Write the modified PDF to a temp file. // Write the modified PDF to a temp file. tempFile = File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_EXTENSION, tempFile = File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_EXTENSION, mContext.getCacheDir()); mContext.getCacheDir()); Loading