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