• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics.pdf;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.graphics.Matrix;
22 import android.graphics.Point;
23 import android.graphics.Rect;
24 import android.os.ParcelFileDescriptor;
25 import android.system.ErrnoException;
26 import android.system.OsConstants;
27 import dalvik.system.CloseGuard;
28 import libcore.io.IoUtils;
29 import libcore.io.Libcore;
30 
31 import java.io.IOException;
32 
33 /**
34  * Class for editing PDF files.
35  *
36  * @hide
37  */
38 public final class PdfEditor {
39 
40     private final CloseGuard mCloseGuard = CloseGuard.get();
41 
42     private final long mNativeDocument;
43 
44     private int mPageCount;
45 
46     private ParcelFileDescriptor mInput;
47 
48     /**
49      * Creates a new instance.
50      * <p>
51      * <strong>Note:</strong> The provided file descriptor must be <strong>seekable</strong>,
52      * i.e. its data being randomly accessed, e.g. pointing to a file. After finishing
53      * with this class you must call {@link #close()}.
54      * </p>
55      * <p>
56      * <strong>Note:</strong> This class takes ownership of the passed in file descriptor
57      * and is responsible for closing it when the editor is closed.
58      * </p>
59      *
60      * @param input Seekable file descriptor to read from.
61      *
62      * @throws java.io.IOException If an error occurs while reading the file.
63      * @throws java.lang.SecurityException If the file requires a password or
64      *         the security scheme is not supported.
65      *
66      * @see #close()
67      */
PdfEditor(@onNull ParcelFileDescriptor input)68     public PdfEditor(@NonNull ParcelFileDescriptor input) throws IOException {
69         if (input == null) {
70             throw new NullPointerException("input cannot be null");
71         }
72 
73         final long size;
74         try {
75             Libcore.os.lseek(input.getFileDescriptor(), 0, OsConstants.SEEK_SET);
76             size = Libcore.os.fstat(input.getFileDescriptor()).st_size;
77         } catch (ErrnoException ee) {
78             throw new IllegalArgumentException("file descriptor not seekable");
79         }
80 
81         mInput = input;
82         mNativeDocument = nativeOpen(mInput.getFd(), size);
83         mPageCount = nativeGetPageCount(mNativeDocument);
84         mCloseGuard.open("close");
85     }
86 
87     /**
88      * Gets the number of pages in the document.
89      *
90      * @return The page count.
91      */
getPageCount()92     public int getPageCount() {
93         throwIfClosed();
94         return mPageCount;
95     }
96 
97     /**
98      * Removes the page with a given index.
99      *
100      * @param pageIndex The page to remove.
101      */
removePage(int pageIndex)102     public void removePage(int pageIndex) {
103         throwIfClosed();
104         throwIfPageNotInDocument(pageIndex);
105         mPageCount = nativeRemovePage(mNativeDocument, pageIndex);
106     }
107 
108     /**
109      * Sets a transformation and clip for a given page. The transformation matrix if
110      * non-null must be affine as per {@link android.graphics.Matrix#isAffine()}. If
111      * the clip is null, then no clipping is performed.
112      *
113      * @param pageIndex The page whose transform to set.
114      * @param transform The transformation to apply.
115      * @param clip The clip to apply.
116      */
setTransformAndClip(int pageIndex, @Nullable Matrix transform, @Nullable Rect clip)117     public void setTransformAndClip(int pageIndex, @Nullable Matrix transform,
118             @Nullable Rect clip) {
119         throwIfClosed();
120         throwIfPageNotInDocument(pageIndex);
121         throwIfNotNullAndNotAfine(transform);
122         if (transform == null) {
123             transform = Matrix.IDENTITY_MATRIX;
124         }
125         if (clip == null) {
126             Point size = new Point();
127             getPageSize(pageIndex, size);
128             nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance,
129                     0, 0, size.x, size.y);
130         } else {
131             nativeSetTransformAndClip(mNativeDocument, pageIndex, transform.native_instance,
132                     clip.left, clip.top, clip.right, clip.bottom);
133         }
134     }
135 
136     /**
137      * Gets the size of a given page in mils (1/72").
138      *
139      * @param pageIndex The page index.
140      * @param outSize The size output.
141      */
getPageSize(int pageIndex, @NonNull Point outSize)142     public void getPageSize(int pageIndex, @NonNull Point outSize) {
143         throwIfClosed();
144         throwIfOutSizeNull(outSize);
145         throwIfPageNotInDocument(pageIndex);
146         nativeGetPageSize(mNativeDocument, pageIndex, outSize);
147     }
148 
149     /**
150      * Gets the media box of a given page in mils (1/72").
151      *
152      * @param pageIndex The page index.
153      * @param outMediaBox The media box output.
154      */
getPageMediaBox(int pageIndex, @NonNull Rect outMediaBox)155     public boolean getPageMediaBox(int pageIndex, @NonNull Rect outMediaBox) {
156         throwIfClosed();
157         throwIfOutMediaBoxNull(outMediaBox);
158         throwIfPageNotInDocument(pageIndex);
159         return nativeGetPageMediaBox(mNativeDocument, pageIndex, outMediaBox);
160     }
161 
162     /**
163      * Sets the media box of a given page in mils (1/72").
164      *
165      * @param pageIndex The page index.
166      * @param mediaBox The media box.
167      */
setPageMediaBox(int pageIndex, @NonNull Rect mediaBox)168     public void setPageMediaBox(int pageIndex, @NonNull Rect mediaBox) {
169         throwIfClosed();
170         throwIfMediaBoxNull(mediaBox);
171         throwIfPageNotInDocument(pageIndex);
172         nativeSetPageMediaBox(mNativeDocument, pageIndex, mediaBox);
173     }
174 
175     /**
176      * Gets the crop box of a given page in mils (1/72").
177      *
178      * @param pageIndex The page index.
179      * @param outCropBox The crop box output.
180      */
getPageCropBox(int pageIndex, @NonNull Rect outCropBox)181     public boolean getPageCropBox(int pageIndex, @NonNull Rect outCropBox) {
182         throwIfClosed();
183         throwIfOutCropBoxNull(outCropBox);
184         throwIfPageNotInDocument(pageIndex);
185         return nativeGetPageCropBox(mNativeDocument, pageIndex, outCropBox);
186     }
187 
188     /**
189      * Sets the crop box of a given page in mils (1/72").
190      *
191      * @param pageIndex The page index.
192      * @param cropBox The crop box.
193      */
setPageCropBox(int pageIndex, @NonNull Rect cropBox)194     public void setPageCropBox(int pageIndex, @NonNull Rect cropBox) {
195         throwIfClosed();
196         throwIfCropBoxNull(cropBox);
197         throwIfPageNotInDocument(pageIndex);
198         nativeSetPageCropBox(mNativeDocument, pageIndex, cropBox);
199     }
200 
201     /**
202      * Gets whether the document prefers to be scaled for printing.
203      *
204      * @return Whether to scale the document.
205      */
shouldScaleForPrinting()206     public boolean shouldScaleForPrinting() {
207         throwIfClosed();
208         return nativeScaleForPrinting(mNativeDocument);
209     }
210 
211     /**
212      * Writes the PDF file to the provided destination.
213      * <p>
214      * <strong>Note:</strong> This method takes ownership of the passed in file
215      * descriptor and is responsible for closing it when writing completes.
216      * </p>
217      * @param output The destination.
218      */
write(ParcelFileDescriptor output)219     public void write(ParcelFileDescriptor output) throws IOException {
220         try {
221             throwIfClosed();
222             nativeWrite(mNativeDocument, output.getFd());
223         } finally {
224             IoUtils.closeQuietly(output);
225         }
226     }
227 
228     /**
229      * Closes this editor. You should not use this instance
230      * after this method is called.
231      */
close()232     public void close() {
233         throwIfClosed();
234         doClose();
235     }
236 
237     @Override
finalize()238     protected void finalize() throws Throwable {
239         try {
240             mCloseGuard.warnIfOpen();
241             if (mInput != null) {
242                 doClose();
243             }
244         } finally {
245             super.finalize();
246         }
247     }
248 
doClose()249     private void doClose() {
250         nativeClose(mNativeDocument);
251         IoUtils.closeQuietly(mInput);
252         mInput = null;
253         mCloseGuard.close();
254     }
255 
throwIfClosed()256     private void throwIfClosed() {
257         if (mInput == null) {
258             throw new IllegalStateException("Already closed");
259         }
260     }
261 
throwIfPageNotInDocument(int pageIndex)262     private void throwIfPageNotInDocument(int pageIndex) {
263         if (pageIndex < 0 || pageIndex >= mPageCount) {
264             throw new IllegalArgumentException("Invalid page index");
265         }
266     }
267 
throwIfNotNullAndNotAfine(Matrix matrix)268     private void throwIfNotNullAndNotAfine(Matrix matrix) {
269         if (matrix != null && !matrix.isAffine()) {
270             throw new IllegalStateException("Matrix must be afine");
271         }
272     }
273 
throwIfOutSizeNull(Point outSize)274     private void throwIfOutSizeNull(Point outSize) {
275         if (outSize == null) {
276             throw new NullPointerException("outSize cannot be null");
277         }
278     }
279 
throwIfOutMediaBoxNull(Rect outMediaBox)280     private void throwIfOutMediaBoxNull(Rect outMediaBox) {
281         if (outMediaBox == null) {
282             throw new NullPointerException("outMediaBox cannot be null");
283         }
284     }
285 
throwIfMediaBoxNull(Rect mediaBox)286     private void throwIfMediaBoxNull(Rect mediaBox) {
287         if (mediaBox == null) {
288             throw new NullPointerException("mediaBox cannot be null");
289         }
290     }
291 
throwIfOutCropBoxNull(Rect outCropBox)292     private void throwIfOutCropBoxNull(Rect outCropBox) {
293         if (outCropBox == null) {
294             throw new NullPointerException("outCropBox cannot be null");
295         }
296     }
297 
throwIfCropBoxNull(Rect cropBox)298     private void throwIfCropBoxNull(Rect cropBox) {
299         if (cropBox == null) {
300             throw new NullPointerException("cropBox cannot be null");
301         }
302     }
303 
nativeOpen(int fd, long size)304     private static native long nativeOpen(int fd, long size);
nativeClose(long documentPtr)305     private static native void nativeClose(long documentPtr);
nativeGetPageCount(long documentPtr)306     private static native int nativeGetPageCount(long documentPtr);
nativeRemovePage(long documentPtr, int pageIndex)307     private static native int nativeRemovePage(long documentPtr, int pageIndex);
nativeWrite(long documentPtr, int fd)308     private static native void nativeWrite(long documentPtr, int fd);
nativeSetTransformAndClip(long documentPtr, int pageIndex, long transformPtr, int clipLeft, int clipTop, int clipRight, int clipBottom)309     private static native void nativeSetTransformAndClip(long documentPtr, int pageIndex,
310             long transformPtr, int clipLeft, int clipTop, int clipRight, int clipBottom);
nativeGetPageSize(long documentPtr, int pageIndex, Point outSize)311     private static native void nativeGetPageSize(long documentPtr, int pageIndex, Point outSize);
nativeGetPageMediaBox(long documentPtr, int pageIndex, Rect outMediaBox)312     private static native boolean nativeGetPageMediaBox(long documentPtr, int pageIndex,
313             Rect outMediaBox);
nativeSetPageMediaBox(long documentPtr, int pageIndex, Rect mediaBox)314     private static native void nativeSetPageMediaBox(long documentPtr, int pageIndex,
315             Rect mediaBox);
nativeGetPageCropBox(long documentPtr, int pageIndex, Rect outMediaBox)316     private static native boolean nativeGetPageCropBox(long documentPtr, int pageIndex,
317             Rect outMediaBox);
nativeSetPageCropBox(long documentPtr, int pageIndex, Rect mediaBox)318     private static native void nativeSetPageCropBox(long documentPtr, int pageIndex,
319             Rect mediaBox);
nativeScaleForPrinting(long documentPtr)320     private static native boolean nativeScaleForPrinting(long documentPtr);
321 }
322