/* * Copyright (C) 2024 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.graphics.Bitmap; import android.graphics.Rect; import android.graphics.pdf.content.PdfPageGotoLinkContent; import android.graphics.pdf.models.FormWidgetInfo; import android.graphics.pdf.models.jni.LinkRects; import android.graphics.pdf.models.jni.LoadPdfResult; import android.graphics.pdf.models.jni.MatchRects; import android.graphics.pdf.models.jni.PageSelection; import android.graphics.pdf.models.jni.SelectionBoundary; import android.graphics.pdf.utils.StrictModeUtils; import android.os.ParcelFileDescriptor; import java.util.List; /** * This class accesses the PdfClient tools to manipulate and render a PDF document. One instance of * this class corresponds to one PDF document, loads it within PdfClient and keeps an internal * reference to the resulting object, to be re-used in subsequent calls. * *

This class is mostly a JNI gateway to PdfClient. * * @hide */ public class PdfDocumentProxy { private static final String TAG = "PdfDocument"; private static final String LIB_NAME = "pdfclient"; /** Internal reference to a native pointer to a Document object. */ private final long mPdfDocPtr; private final int mNumPages; /** Constructs a PdfDocument. Do not call directly from java, use {@link #createFromFd}. */ protected PdfDocumentProxy(long pdfDocPtr, int numPages) { this.mPdfDocPtr = pdfDocPtr; this.mNumPages = numPages; } /** * Tries to load a PdfDocument from native file descriptor. * * @return a LoadPdfResult of status LOADED containing the PdfDocument, * or, an empty LoadPdfResult of a different status indicating failure. */ public static native LoadPdfResult createFromFd(int fd, String password); /** * Loads the PdfClient binary library used to render PDF documents. The library will only be * loaded once so subsequent calls after the first will have no effect. This may be used to * preload the library before use. */ public static void loadLibPdf() { // TODO(b/324549320): Cleanup if bypassing is not required StrictModeUtils.bypass(() -> System.loadLibrary(LIB_NAME)); } public long getPdfDocPtr() { return mPdfDocPtr; } public int getNumPages() { return mNumPages; } /** Destroys the PDF document and release resources held by PdfClient. */ public native void destroy(); /** * Tries to save this PdfDocument to the given native file descriptor, which must be open for * write or append. * * @return true on success */ public native boolean saveToFd(int fd); /** * Saves the current state of this {@link PdfDocument} to the given, writable, file descriptor. * The given file descriptor is closed by this function. * * @param destination the file descriptor to write to * @return true on success */ public boolean saveAs(ParcelFileDescriptor destination) { return saveToFd(destination.detachFd()); } /** * Returns the width of the given page of the PDF. This is measured in points, but we * zoom-to-fit, so it doesn't matter. */ public native int getPageWidth(int pageNum); /** * Returns the height of the given page of the PDF. This is measured in points, but we * zoom-to-fit, so it doesn't matter. */ public native int getPageHeight(int pageNum); /** * Renders a page to a bitmap. * * @param pageNum the page number of the page to be rendered * @param clipLeft the left coordinate of the clipping boundary in bitmap coordinates * @param clipTop the top coordinate of the clipping boundary in bitmap coordinates * @param clipRight the right coordinate of the clipping boundary in bitmap coordinates * @param clipBottom the bottom coordinate of the clipping boundary in bitmap coordinates * @param transform an affine transform matrix in the form of an array. * @see android.graphics.Matrix#getValues(float[]) * @param renderMode the render mode * @param hideTextAnnots whether to hide text and highlight annotations * @return true if the page was rendered into the destination bitmap */ public native boolean render( int pageNum, Bitmap bitmap, int clipLeft, int clipTop, int clipRight, int clipBottom, float[] transform, int renderMode, boolean hideTextAnnots); /** * Clones the currently loaded document using the provided file descriptor. *

You are required to detach the file descriptor as the native code will close it. * * @param destination native fd pointer * @return true if the cloning was successful */ private native boolean cloneWithoutSecurity(int destination); /** * Clones the currently loaded document using the provided file descriptor. *

You are required to detach the file descriptor as the native code will close it. * * @param destination {@link ParcelFileDescriptor} to which the document needs to be written to. * @return true if the cloning was successful */ public boolean cloneWithoutSecurity(ParcelFileDescriptor destination) { return cloneWithoutSecurity(destination.detachFd()); } /** * Gets the text of the entire page as a string, in the order the text is * found in the PDF stream. */ public native String getPageText(int pageNum); /** * Gets all pieces of alt-text found for the page, in the order the alt-text is found in the * PDF stream. */ public native List getPageAltText(int pageNum); /** * Searches for the given string on the page and returns the bounds of all of the matches. * The number of matches is {@link MatchRects#size()}. */ public native MatchRects searchPageText(int pageNum, String query); /** * Get the text selection that spans between the two boundaries (inclusive of start and * exclusive of stop), both of which can be either exactly defined with text indexes, or * approximately defined with points on the page. The resulting selection will also be exactly * defined with both indexes and points. If the start and stop boundary are both the same point, * selects the word at that point. */ public native PageSelection selectPageText(int pageNum, SelectionBoundary start, SelectionBoundary stop); /** Get the bounds and URLs of all the links on the given page. */ public native LinkRects getPageLinks(int pageNum); /** Returns bookmarks and other goto links (within the current document) on a page */ public native List getPageGotoLinks(int pageNum); /** Loads a page object and retains it in memory when a page becomes visible. */ public native void retainPage(int pageNum); /** Cleans up objects in memory related to a page after it is no longer visible. */ public native void releasePage(int pageNum); /** Returns true if the PDF is linearized. (May give false negatives for <1KB PDFs). */ public native boolean isPdfLinearized(); /** Returns true if the document prefers to be scaled for printing. */ public native boolean scaleForPrinting(); /** * Returns an int representing the form type contained in the PDF, e.g. Acro vs XFA (if any). */ public native int getFormType(); /** Obtains information about the widget at point ({@code x}, {@code y}), if any. */ public native FormWidgetInfo getFormWidgetInfo(int pageNum, int x, int y); /** * Obtains information about the widget with ({@code annotationIndex} on page {@code pageNum}), * if any. */ public native FormWidgetInfo getFormWidgetInfo(int pageNum, int annotationIndex); /** * Obtains information about all form widgets on page ({@code pageNum}, if any. * *

Optionally restricts by {@code typeIds}. If {@code typeIds} is empty, all form widgets on * the page will be returned. */ public native List getFormWidgetInfos(int pageNum, int[] typeIds); /** * Executes an interactive click on the page at the given point ({@code x}, {@code y}). * * @return rectangular areas of the page bitmap that have been invalidated by this action */ public native List clickOnPage(int pageNum, int x, int y); /** * Sets the text of the widget at {@code annotationIndex}, if applicable. * * @return rectangular areas of the page bitmap that have been invalidated by this action */ public native List setFormFieldText(int pageNum, int annotIndex, String text); /** * Selects the {@code selectedIndices} and unselects all others for the widget at {@code * annotationIndex}, if applicable. * * @return Rectangular areas of the page bitmap that have been invalidated by this action */ public native List setFormFieldSelectedIndices( int pageNum, int annotIndex, int[] selectedIndices); }