1 /*
2  * Copyright (C) 2024 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.graphics.Bitmap;
20 import android.graphics.Rect;
21 import android.graphics.pdf.content.PdfPageGotoLinkContent;
22 import android.graphics.pdf.models.FormWidgetInfo;
23 import android.graphics.pdf.models.jni.LinkRects;
24 import android.graphics.pdf.models.jni.LoadPdfResult;
25 import android.graphics.pdf.models.jni.MatchRects;
26 import android.graphics.pdf.models.jni.PageSelection;
27 import android.graphics.pdf.models.jni.SelectionBoundary;
28 import android.graphics.pdf.utils.StrictModeUtils;
29 import android.os.ParcelFileDescriptor;
30 
31 import java.util.List;
32 
33 /**
34  * This class accesses the PdfClient tools to manipulate and render a PDF document. One instance of
35  * this class corresponds to one PDF document, loads it within PdfClient and keeps an internal
36  * reference to the resulting object, to be re-used in subsequent calls.
37  *
38  * <p>This class is mostly a JNI gateway to PdfClient.
39  *
40  * @hide
41  */
42 public class PdfDocumentProxy {
43     private static final String TAG = "PdfDocument";
44 
45     private static final String LIB_NAME = "pdfclient";
46 
47     /** Internal reference to a native pointer to a Document object. */
48     private final long mPdfDocPtr;
49 
50     private final int mNumPages;
51 
52     /** Constructs a PdfDocument. Do not call directly from java, use {@link #createFromFd}. */
PdfDocumentProxy(long pdfDocPtr, int numPages)53     protected PdfDocumentProxy(long pdfDocPtr, int numPages) {
54         this.mPdfDocPtr = pdfDocPtr;
55         this.mNumPages = numPages;
56     }
57 
58     /**
59      * Tries to load a PdfDocument from native file descriptor.
60      *
61      * @return a LoadPdfResult of status LOADED containing the PdfDocument,
62      * or, an empty LoadPdfResult of a different status indicating failure.
63      */
createFromFd(int fd, String password)64     public static native LoadPdfResult createFromFd(int fd, String password);
65 
66     /**
67      * Loads the PdfClient binary library used to render PDF documents. The library will only be
68      * loaded once so subsequent calls after the first will have no effect. This may be used to
69      * preload the library before use.
70      */
loadLibPdf()71     public static void loadLibPdf() {
72         // TODO(b/324549320): Cleanup if bypassing is not required
73         StrictModeUtils.bypass(() -> System.loadLibrary(LIB_NAME));
74     }
75 
getPdfDocPtr()76     public long getPdfDocPtr() {
77         return mPdfDocPtr;
78     }
79 
getNumPages()80     public int getNumPages() {
81         return mNumPages;
82     }
83 
84     /** Destroys the PDF document and release resources held by PdfClient. */
destroy()85     public native void destroy();
86 
87     /**
88      * Tries to save this PdfDocument to the given native file descriptor, which must be open for
89      * write or append.
90      *
91      * @return true on success
92      */
saveToFd(int fd)93     public native boolean saveToFd(int fd);
94 
95     /**
96      * Saves the current state of this {@link PdfDocument} to the given, writable, file descriptor.
97      * The given file descriptor is closed by this function.
98      *
99      * @param destination the file descriptor to write to
100      * @return true on success
101      */
saveAs(ParcelFileDescriptor destination)102     public boolean saveAs(ParcelFileDescriptor destination) {
103         return saveToFd(destination.detachFd());
104     }
105 
106     /**
107      * Returns the width of the given page of the PDF. This is measured in points, but we
108      * zoom-to-fit, so it doesn't matter.
109      */
getPageWidth(int pageNum)110     public native int getPageWidth(int pageNum);
111 
112     /**
113      * Returns the height of the given page of the PDF. This is measured in points, but we
114      * zoom-to-fit, so it doesn't matter.
115      */
getPageHeight(int pageNum)116     public native int getPageHeight(int pageNum);
117 
118     /**
119      * Renders a page to a bitmap.
120      *
121      * @param pageNum the page number of the page to be rendered
122      * @param clipLeft the left coordinate of the clipping boundary in bitmap coordinates
123      * @param clipTop the top coordinate of the clipping boundary in bitmap coordinates
124      * @param clipRight the right coordinate of the clipping boundary in bitmap coordinates
125      * @param clipBottom the bottom coordinate of the clipping boundary in bitmap coordinates
126      * @param transform an affine transform matrix in the form of an array.
127      * @see android.graphics.Matrix#getValues(float[])
128      * @param renderMode the render mode
129      * @param showAnnotTypes Bitmask of renderFlags to indicate the types of annotations to
130      *                       be rendered
131      * @param renderFormFields true to included PDF form content in the output
132      * @return true if the page was rendered into the destination bitmap
133      */
render( int pageNum, Bitmap bitmap, int clipLeft, int clipTop, int clipRight, int clipBottom, float[] transform, int renderMode, int showAnnotTypes, boolean renderFormFields)134     public native boolean render(
135             int pageNum,
136             Bitmap bitmap,
137             int clipLeft,
138             int clipTop,
139             int clipRight,
140             int clipBottom,
141             float[] transform,
142             int renderMode,
143             int showAnnotTypes,
144             boolean renderFormFields);
145 
146     /**
147      * Clones the currently loaded document using the provided file descriptor.
148      * <p>You are required to detach the file descriptor as the native code will close it.
149      *
150      * @param destination native fd pointer
151      * @return true if the cloning was successful
152      */
cloneWithoutSecurity(int destination)153     private native boolean cloneWithoutSecurity(int destination);
154 
155     /**
156      * Clones the currently loaded document using the provided file descriptor.
157      * <p>You are required to detach the file descriptor as the native code will close it.
158      *
159      * @param destination {@link ParcelFileDescriptor} to which the document needs to be written to.
160      * @return true if the cloning was successful
161      */
cloneWithoutSecurity(ParcelFileDescriptor destination)162     public boolean cloneWithoutSecurity(ParcelFileDescriptor destination) {
163         return cloneWithoutSecurity(destination.detachFd());
164     }
165 
166     /**
167      * Gets the text of the entire page as a string, in the order the text is
168      * found in the PDF stream.
169      */
getPageText(int pageNum)170     public native String getPageText(int pageNum);
171 
172     /**
173      * Gets all pieces of alt-text found for the page, in the order the alt-text is found in the
174      * PDF stream.
175      */
getPageAltText(int pageNum)176     public native List<String> getPageAltText(int pageNum);
177 
178     /**
179      * Searches for the given string on the page and returns the bounds of all of the matches.
180      * The number of matches is {@link MatchRects#size()}.
181      */
searchPageText(int pageNum, String query)182     public native MatchRects searchPageText(int pageNum, String query);
183 
184     /**
185      * Get the text selection that spans between the two boundaries (inclusive of start and
186      * exclusive of stop), both of which can be either exactly defined with text indexes, or
187      * approximately defined with points on the page. The resulting selection will also be exactly
188      * defined with both indexes and points. If the start and stop boundary are both the same point,
189      * selects the word at that point.
190      */
selectPageText(int pageNum, SelectionBoundary start, SelectionBoundary stop)191     public native PageSelection selectPageText(int pageNum, SelectionBoundary start,
192             SelectionBoundary stop);
193 
194     /** Get the bounds and URLs of all the links on the given page. */
getPageLinks(int pageNum)195     public native LinkRects getPageLinks(int pageNum);
196 
197     /** Returns bookmarks and other goto links (within the current document) on a page */
getPageGotoLinks(int pageNum)198     public native List<PdfPageGotoLinkContent> getPageGotoLinks(int pageNum);
199 
200     /** Loads a page object and retains it in memory when a page becomes visible. */
retainPage(int pageNum)201     public native void retainPage(int pageNum);
202 
203     /** Cleans up objects in memory related to a page after it is no longer visible. */
releasePage(int pageNum)204     public native void releasePage(int pageNum);
205 
206     /** Returns true if the PDF is linearized. (May give false negatives for <1KB PDFs). */
isPdfLinearized()207     public native boolean isPdfLinearized();
208 
209     /** Returns true if the document prefers to be scaled for printing. */
scaleForPrinting()210     public native boolean scaleForPrinting();
211 
212     /**
213      * Returns an int representing the form type contained in the PDF, e.g. Acro vs XFA (if any).
214      */
getFormType()215     public native int getFormType();
216 
217     /** Obtains information about the widget at point ({@code x}, {@code y}), if any. */
getFormWidgetInfo(int pageNum, int x, int y)218     public native FormWidgetInfo getFormWidgetInfo(int pageNum, int x, int y);
219 
220     /**
221      * Obtains information about the widget with ({@code annotationIndex} on page {@code pageNum}),
222      * if any.
223      */
getFormWidgetInfo(int pageNum, int annotationIndex)224     public native FormWidgetInfo getFormWidgetInfo(int pageNum, int annotationIndex);
225 
226     /**
227      * Obtains information about all form widgets on page ({@code pageNum}, if any.
228      *
229      * <p>Optionally restricts by {@code typeIds}. If {@code typeIds} is empty, all form widgets on
230      * the page will be returned.
231      */
getFormWidgetInfos(int pageNum, int[] typeIds)232     public native List<FormWidgetInfo> getFormWidgetInfos(int pageNum, int[] typeIds);
233 
234     /**
235      * Executes an interactive click on the page at the given point ({@code x}, {@code y}).
236      *
237      * @return rectangular areas of the page bitmap that have been invalidated by this action
238      */
clickOnPage(int pageNum, int x, int y)239     public native List<Rect> clickOnPage(int pageNum, int x, int y);
240 
241     /**
242      * Sets the text of the widget at {@code annotationIndex}, if applicable.
243      *
244      * @return rectangular areas of the page bitmap that have been invalidated by this action
245      */
setFormFieldText(int pageNum, int annotIndex, String text)246     public native List<Rect> setFormFieldText(int pageNum, int annotIndex, String text);
247 
248     /**
249      * Selects the {@code selectedIndices} and unselects all others for the widget at {@code
250      * annotationIndex}, if applicable.
251      *
252      * @return Rectangular areas of the page bitmap that have been invalidated by this action
253      */
setFormFieldSelectedIndices( int pageNum, int annotIndex, int[] selectedIndices)254     public native List<Rect> setFormFieldSelectedIndices(
255             int pageNum, int annotIndex, int[] selectedIndices);
256 }
257