377 lines
11 KiB
Java
377 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.graphics.pdf;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.Point;
|
|
import android.graphics.Rect;
|
|
import android.os.ParcelFileDescriptor;
|
|
import android.system.ErrnoException;
|
|
import android.system.Os;
|
|
import android.system.OsConstants;
|
|
|
|
import dalvik.system.CloseGuard;
|
|
|
|
import libcore.io.IoUtils;
|
|
|
|
import java.io.IOException;
|
|
|
|
/**
|
|
* Class for editing PDF files.
|
|
*
|
|
* @hide
|
|
*/
|
|
public final class PdfEditor {
|
|
|
|
/**
|
|
* Any call the native pdfium code has to be single threaded as the library does not support
|
|
* parallel use.
|
|
*/
|
|
private static final Object sPdfiumLock = new Object();
|
|
|
|
private final CloseGuard mCloseGuard = CloseGuard.get();
|
|
|
|
private long mNativeDocument;
|
|
|
|
private int mPageCount;
|
|
|
|
private ParcelFileDescriptor mInput;
|
|
|
|
/**
|
|
* Creates a new instance.
|
|
* <p>
|
|
* <strong>Note:</strong> The provided file descriptor must be <strong>seekable</strong>,
|
|
* i.e. its data being randomly accessed, e.g. pointing to a file. After finishing
|
|
* with this class you must call {@link #close()}.
|
|
* </p>
|
|
* <p>
|
|
* <strong>Note:</strong> This class takes ownership of the passed in file descriptor
|
|
* and is responsible for closing it when the editor is closed.
|
|
* </p>
|
|
*
|
|
* @param input Seekable file descriptor to read from.
|
|
*
|
|
* @throws java.io.IOException If an error occurs while reading the file.
|
|
* @throws java.lang.SecurityException If the file requires a password or
|
|
* the security scheme is not supported.
|
|
*
|
|
* @see #close()
|
|
*/
|
|
public PdfEditor(@NonNull ParcelFileDescriptor input) throws IOException {
|
|
if (input == null) {
|
|
throw new NullPointerException("input cannot be null");
|
|
}
|
|
|
|
final long size;
|
|
try {
|
|
Os.lseek(input.getFileDescriptor(), 0, OsConstants.SEEK_SET);
|
|
size = Os.fstat(input.getFileDescriptor()).st_size;
|
|
} catch (ErrnoException ee) {
|
|
throw new IllegalArgumentException("file descriptor not seekable");
|
|
}
|
|
mInput = input;
|
|
|
|
synchronized (sPdfiumLock) {
|
|
mNativeDocument = nativeOpen(mInput.getFd(), size);
|
|
try {
|
|
mPageCount = nativeGetPageCount(mNativeDocument);
|
|
} catch (Throwable t) {
|
|
nativeClose(mNativeDocument);
|
|
mNativeDocument = 0;
|
|
throw t;
|
|
}
|
|
}
|
|
|
|
mCloseGuard.open("close");
|
|
}
|
|
|
|
/**
|
|
* Gets the number of pages in the document.
|
|
*
|
|
* @return The page count.
|
|
*/
|
|
public int getPageCount() {
|
|
throwIfClosed();
|
|
return mPageCount;
|
|
}
|
|
|
|
/**
|
|
* Removes the page with a given index.
|
|
*
|
|
* @param pageIndex The page to remove.
|
|
*/
|
|
public void removePage(int pageIndex) {
|
|
throwIfClosed();
|
|
throwIfPageNotInDocument(pageIndex);
|
|
|
|
synchronized (sPdfiumLock) {
|
|
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);
|
|
|
|
synchronized (sPdfiumLock) {
|
|
nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.ni(),
|
|
0, 0, size.x, size.y);
|
|
}
|
|
} else {
|
|
synchronized (sPdfiumLock) {
|
|
nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.ni(),
|
|
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);
|
|
|
|
synchronized (sPdfiumLock) {
|
|
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);
|
|
|
|
synchronized (sPdfiumLock) {
|
|
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);
|
|
|
|
synchronized (sPdfiumLock) {
|
|
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);
|
|
|
|
synchronized (sPdfiumLock) {
|
|
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);
|
|
|
|
synchronized (sPdfiumLock) {
|
|
nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets whether the document prefers to be scaled for printing.
|
|
*
|
|
* @return Whether to scale the document.
|
|
*/
|
|
public boolean shouldScaleForPrinting() {
|
|
throwIfClosed();
|
|
|
|
synchronized (sPdfiumLock) {
|
|
return nativeScaleForPrinting(mNativeDocument);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the PDF file to the provided destination.
|
|
* <p>
|
|
* <strong>Note:</strong> This method takes ownership of the passed in file
|
|
* descriptor and is responsible for closing it when writing completes.
|
|
* </p>
|
|
* @param output The destination.
|
|
*/
|
|
public void write(ParcelFileDescriptor output) throws IOException {
|
|
try {
|
|
throwIfClosed();
|
|
|
|
synchronized (sPdfiumLock) {
|
|
nativeWrite(mNativeDocument, output.getFd());
|
|
}
|
|
} finally {
|
|
IoUtils.closeQuietly(output);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes this editor. You should not use this instance
|
|
* after this method is called.
|
|
*/
|
|
public void close() {
|
|
throwIfClosed();
|
|
doClose();
|
|
}
|
|
|
|
@Override
|
|
protected void finalize() throws Throwable {
|
|
try {
|
|
if (mCloseGuard != null) {
|
|
mCloseGuard.warnIfOpen();
|
|
}
|
|
|
|
doClose();
|
|
} finally {
|
|
super.finalize();
|
|
}
|
|
}
|
|
|
|
private void doClose() {
|
|
if (mNativeDocument != 0) {
|
|
synchronized (sPdfiumLock) {
|
|
nativeClose(mNativeDocument);
|
|
}
|
|
mNativeDocument = 0;
|
|
}
|
|
|
|
if (mInput != null) {
|
|
IoUtils.closeQuietly(mInput);
|
|
mInput = null;
|
|
}
|
|
mCloseGuard.close();
|
|
}
|
|
|
|
private void throwIfClosed() {
|
|
if (mInput == null) {
|
|
throw new IllegalStateException("Already closed");
|
|
}
|
|
}
|
|
|
|
private void throwIfPageNotInDocument(int pageIndex) {
|
|
if (pageIndex < 0 || pageIndex >= mPageCount) {
|
|
throw new IllegalArgumentException("Invalid page index");
|
|
}
|
|
}
|
|
|
|
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 void nativeClose(long documentPtr);
|
|
private static native int nativeGetPageCount(long documentPtr);
|
|
private static native int nativeRemovePage(long documentPtr, int pageIndex);
|
|
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);
|
|
}
|