am 93134ce8: Merge "Save to a PDF file should look like print preview." into lmp-mr1-dev
automerge: 160021d
* commit '160021d8cf62f566587d899498c72fff82b6969f':
Save to a PDF file should look like print preview.
This commit is contained in:
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public final class PdfManipulationService extends Service {
|
|||||||
}
|
}
|
||||||
mRenderer = new PdfRenderer(source);
|
mRenderer = new PdfRenderer(source);
|
||||||
return mRenderer.getPageCount();
|
return mRenderer.getPageCount();
|
||||||
} catch (IOException|IllegalStateException e) {
|
} catch (IOException | IllegalStateException e) {
|
||||||
IoUtils.closeQuietly(source);
|
IoUtils.closeQuietly(source);
|
||||||
Log.e(LOG_TAG, "Cannot open file", e);
|
Log.e(LOG_TAG, "Cannot open file", e);
|
||||||
return MALFORMED_PDF_FILE_ERROR;
|
return MALFORMED_PDF_FILE_ERROR;
|
||||||
@@ -217,7 +217,7 @@ public final class PdfManipulationService extends Service {
|
|||||||
}
|
}
|
||||||
mEditor = new PdfEditor(source);
|
mEditor = new PdfEditor(source);
|
||||||
return mEditor.getPageCount();
|
return mEditor.getPageCount();
|
||||||
} catch (IOException|IllegalStateException e) {
|
} catch (IOException | IllegalStateException e) {
|
||||||
IoUtils.closeQuietly(source);
|
IoUtils.closeQuietly(source);
|
||||||
Log.e(LOG_TAG, "Cannot open file", e);
|
Log.e(LOG_TAG, "Cannot open file", e);
|
||||||
throw new RemoteException(e.toString());
|
throw new RemoteException(e.toString());
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
Reference in New Issue
Block a user