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

Commit 2a6749ad authored by Svetoslav's avatar Svetoslav Committed by android-build-merger
Browse files

am 93134ce8: Merge "Save to a PDF file should look like print preview." into lmp-mr1-dev

automerge: 160021d8

* commit '160021d8':
  Save to a PDF file should look like print preview.
parents eaaa38ae 160021d8
Loading
Loading
Loading
Loading
+202 −2
Original line number Original line Diff line number Diff line
@@ -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>
@@ -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;
@@ -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;
};
};


};
};
+155 −0
Original line number Original line Diff line number Diff line
@@ -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;
@@ -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>
@@ -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);
}
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -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.
@@ -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();
}
}
+107 −2
Original line number Original line Diff line number Diff line
@@ -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) {
+25 −13
Original line number Original line Diff line number Diff line
@@ -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) {
@@ -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);
        }
        }
    }
    }


@@ -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) {
@@ -1606,7 +1609,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
                }
                }
                doFinish();
                doFinish();
            }
            }
        }).shred();
        }).transform();
    }
    }


    private void doFinish() {
    private void doFinish() {
@@ -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";


@@ -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;
            }
            }
@@ -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);
@@ -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;
@@ -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());