12961769ea94f69c191a2dd785b2504666c7292d0Svetoslav/*
22961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * Copyright (C) 2014 The Android Open Source Project
32961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
42961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * Licensed under the Apache License, Version 2.0 (the "License");
52961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * you may not use this file except in compliance with the License.
62961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * You may obtain a copy of the License at
72961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
82961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *      http://www.apache.org/licenses/LICENSE-2.0
92961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
102961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * Unless required by applicable law or agreed to in writing, software
112961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * distributed under the License is distributed on an "AS IS" BASIS,
122961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * See the License for the specific language governing permissions and
142961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * limitations under the License.
152961769ea94f69c191a2dd785b2504666c7292d0Svetoslav */
162961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
172961769ea94f69c191a2dd785b2504666c7292d0Svetoslavpackage android.graphics.pdf;
182961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
192961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.annotation.IntDef;
202961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.annotation.NonNull;
212961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.annotation.Nullable;
222961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.graphics.Bitmap;
232961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.graphics.Bitmap.Config;
242961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.graphics.Matrix;
252961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.graphics.Point;
262961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.graphics.Rect;
272961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.os.ParcelFileDescriptor;
282961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.system.ErrnoException;
292961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport android.system.OsConstants;
302961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport dalvik.system.CloseGuard;
312961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport libcore.io.Libcore;
322961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
332961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport java.io.IOException;
342961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport java.lang.annotation.Retention;
352961769ea94f69c191a2dd785b2504666c7292d0Svetoslavimport java.lang.annotation.RetentionPolicy;
362961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
372961769ea94f69c191a2dd785b2504666c7292d0Svetoslav/**
382961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * <p>
392961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * This class enables rendering a PDF document. This class is not thread safe.
402961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * </p>
412961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * <p>
422961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * If you want to render a PDF, you create a renderer and for every page you want
432961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * to render, you open the page, render it, and close the page. After you are done
442961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * with rendering, you close the renderer. After the renderer is closed it should not
452961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * be used anymore. Note that the pages are rendered one by one, i.e. you can have
462961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * only a single page opened at any given time.
472961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * </p>
482961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * <p>
492961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * A typical use of the APIs to render a PDF looks like this:
502961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * </p>
512961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * <pre>
522961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * // create a new renderer
532961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * PdfRenderer renderer = new PdfRenderer(getSeekableFileDescriptor());
542961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
552961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * // let us just render all pages
562961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * final int pageCount = renderer.getPageCount();
572961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * for (int i = 0; i < pageCount; i++) {
582961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *     Page page = renderer.openPage(i);
592961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
602961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *     // say we render for showing on the screen
6195b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav *     page.render(mBitmap, null, null, Page.RENDER_MODE_FOR_DISPLAY);
622961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
632961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *     // do stuff with the bitmap
642961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
6595b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav *     // close the page
6695b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav *     page.close();
672961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * }
682961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
692961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * // close the renderer
702961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * renderer.close();
712961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * </pre>
722961769ea94f69c191a2dd785b2504666c7292d0Svetoslav *
73525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * <h3>Print preview and print output</h3>
74525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * <p>
75525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * If you are using this class to rasterize a PDF for printing or show a print
76525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * preview, it is recommended that you respect the following contract in order
77525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * to provide a consistent user experience when seeing a preview and printing,
78525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * i.e. the user sees a preview that is the same as the printout.
79525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * </p>
80525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * <ul>
81525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * <li>
82525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Respect the property whether the document would like to be scaled for printing
83525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * as per {@link #shouldScaleForPrinting()}.
84525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * </li>
85525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * <li>
86525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * When scaling a document for printing the aspect ratio should be preserved.
87525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * </li>
88525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * <li>
89525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Do not inset the content with any margins from the {@link android.print.PrintAttributes}
90525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * as the application is responsible to render it such that the margins are respected.
91525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * </li>
92525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * <li>
93525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * If document page size is greater than the printed media size the content should
94525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * be anchored to the upper left corner of the page for left-to-right locales and
95525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * top right corner for right-to-left locales.
96525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * </li>
97525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * </ul>
98525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *
992961769ea94f69c191a2dd785b2504666c7292d0Svetoslav * @see #close()
1002961769ea94f69c191a2dd785b2504666c7292d0Svetoslav */
1012961769ea94f69c191a2dd785b2504666c7292d0Svetoslavpublic final class PdfRenderer implements AutoCloseable {
1022961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private final CloseGuard mCloseGuard = CloseGuard.get();
1032961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1042961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private final Point mTempPoint = new Point();
1052961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1062961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private final long mNativeDocument;
1072961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1082961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private final int mPageCount;
1092961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1102961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private ParcelFileDescriptor mInput;
1112961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1122961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private Page mCurrentPage;
1132961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1142961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    /** @hide */
1152961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    @IntDef({
1162961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        Page.RENDER_MODE_FOR_DISPLAY,
1172961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        Page.RENDER_MODE_FOR_PRINT
1182961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    })
1192961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    @Retention(RetentionPolicy.SOURCE)
1202961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    public @interface RenderMode {}
1212961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1222961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    /**
1232961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * Creates a new instance.
1242961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * <p>
1252961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * <strong>Note:</strong> The provided file descriptor must be <strong>seekable</strong>,
1262961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * i.e. its data being randomly accessed, e.g. pointing to a file.
1272961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * </p>
1282961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * <p>
1292961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * <strong>Note:</strong> This class takes ownership of the passed in file descriptor
1302961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * and is responsible for closing it when the renderer is closed.
1312961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * </p>
1322961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     *
1332961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * @param input Seekable file descriptor to read from.
134fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov     *
135fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov     * @throws java.io.IOException If an error occurs while reading the file.
136fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov     * @throws java.lang.SecurityException If the file requires a password or
137fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov     *         the security scheme is not supported.
1382961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     */
1392961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    public PdfRenderer(@NonNull ParcelFileDescriptor input) throws IOException {
1402961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        if (input == null) {
1412961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            throw new NullPointerException("input cannot be null");
1422961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
1432961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1442961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        final long size;
1452961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        try {
1462961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            Libcore.os.lseek(input.getFileDescriptor(), 0, OsConstants.SEEK_SET);
1472961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            size = Libcore.os.fstat(input.getFileDescriptor()).st_size;
1482961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        } catch (ErrnoException ee) {
1492961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            throw new IllegalArgumentException("file descriptor not seekable");
1502961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
1512961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1522961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        mInput = input;
1532961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        mNativeDocument = nativeCreate(mInput.getFd(), size);
1542961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        mPageCount = nativeGetPageCount(mNativeDocument);
1552961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        mCloseGuard.open("close");
1562961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
1572961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1582961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    /**
1592961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * Closes this renderer. You should not use this instance
1602961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * after this method is called.
1612961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     */
1622961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    public void close() {
1632961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        throwIfClosed();
1642961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        throwIfPageOpened();
1652961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        doClose();
1662961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
1672961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1682961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    /**
1692961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * Gets the number of pages in the document.
1702961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     *
1712961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * @return The page count.
1722961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     */
1732961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    public int getPageCount() {
1742961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        throwIfClosed();
1752961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        return mPageCount;
1762961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
1772961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1782961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    /**
1792961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * Gets whether the document prefers to be scaled for printing.
1802961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * You should take this info account if the document is rendered
1812961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * for printing and the target media size differs from the page
1822961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * size.
1832961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     *
1842961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * @return If to scale the document.
1852961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     */
1862961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    public boolean shouldScaleForPrinting() {
1872961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        throwIfClosed();
1882961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        return nativeScaleForPrinting(mNativeDocument);
1892961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
1902961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
1912961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    /**
1922961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * Opens a page for rendering.
1932961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     *
1942961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * @param index The page index.
1952961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * @return A page that can be rendered.
1962961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     *
19795b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav     * @see android.graphics.pdf.PdfRenderer.Page#close() PdfRenderer.Page.close()
1982961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     */
1992961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    public Page openPage(int index) {
2002961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        throwIfClosed();
2012961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        throwIfPageOpened();
20213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov        throwIfPageNotInDocument(index);
2032961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        mCurrentPage = new Page(index);
2042961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        return mCurrentPage;
2052961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
2062961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2072961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    @Override
2082961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    protected void finalize() throws Throwable {
2092961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        try {
2102961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            mCloseGuard.warnIfOpen();
2112961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            if (mInput != null) {
2122961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                doClose();
2132961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            }
2142961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        } finally {
2152961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            super.finalize();
2162961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
2172961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
2182961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2192961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private void doClose() {
2202961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        if (mCurrentPage != null) {
2212961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            mCurrentPage.close();
2222961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
2232961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        nativeClose(mNativeDocument);
2242961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        try {
2252961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            mInput.close();
2262961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        } catch (IOException ioe) {
2272961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            /* ignore - best effort */
2282961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
2292961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        mInput = null;
2302961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        mCloseGuard.close();
2312961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
2322961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2332961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private void throwIfClosed() {
2342961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        if (mInput == null) {
2352961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            throw new IllegalStateException("Already closed");
2362961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
2372961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
2382961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2392961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private void throwIfPageOpened() {
2402961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        if (mCurrentPage != null) {
2412961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            throw new IllegalStateException("Current page not closed");
2422961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
2432961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
2442961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
24513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov    private void throwIfPageNotInDocument(int pageIndex) {
24662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav        if (pageIndex < 0 || pageIndex >= mPageCount) {
24713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov            throw new IllegalArgumentException("Invalid page index");
24813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov        }
24913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov    }
25013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov
2512961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    /**
2522961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     * This class represents a PDF document page for rendering.
2532961769ea94f69c191a2dd785b2504666c7292d0Svetoslav     */
25495b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav    public final class Page implements AutoCloseable {
25595b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav
25695b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        private final CloseGuard mCloseGuard = CloseGuard.get();
2572961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2582961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        /**
2592961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * Mode to render the content for display on a screen.
2602961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         */
2612961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        public static final int RENDER_MODE_FOR_DISPLAY = 1;
2622961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2632961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        /**
2642961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * Mode to render the content for printing.
2652961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         */
2662961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        public static final int RENDER_MODE_FOR_PRINT = 2;
2672961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2682961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        private final int mIndex;
2692961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        private final int mWidth;
2702961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        private final int mHeight;
2712961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2722961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        private long mNativePage;
2732961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2742961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        private Page(int index) {
2752961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            Point size = mTempPoint;
2762961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            mNativePage = nativeOpenPageAndGetSize(mNativeDocument, index, size);
2772961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            mIndex = index;
2782961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            mWidth = size.x;
2792961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            mHeight = size.y;
28095b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            mCloseGuard.open("close");
2812961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
2822961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2832961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        /**
2842961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * Gets the page index.
2852961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         *
2862961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @return The index.
2872961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         */
2882961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        public int getIndex() {
2892961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            return  mIndex;
2902961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
2912961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
2922961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        /**
2932961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * Gets the page width in points (1/72").
2942961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         *
2952961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @return The width in points.
2962961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         */
2972961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        public int getWidth() {
2982961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            return mWidth;
2992961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
3002961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3012961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        /**
3022961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * Gets the page height in points (1/72").
3032961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         *
3042961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @return The height in points.
3052961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         */
3062961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        public int getHeight() {
3072961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            return mHeight;
3082961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
3092961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3102961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        /**
3112961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * Renders a page to a bitmap.
3122961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * <p>
3132961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * You may optionally specify a rectangular clip in the bitmap bounds. No rendering
3142961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * outside the clip will be performed, hence it is your responsibility to initialize
3152961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * the bitmap outside the clip.
3162961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * </p>
3172961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * <p>
3182961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * You may optionally specify a matrix to transform the content from page coordinates
319525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov         * which are in points (1/72") to bitmap coordinates which are in pixels. If this
3202961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * matrix is not provided this method will apply a transformation that will fit the
32195b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav         * whole page to the destination clip if provided or the destination bitmap if no
3222961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * clip is provided.
3232961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * </p>
3242961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * <p>
3252961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * The clip and transformation are useful for implementing tile rendering where the
3262961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * destination bitmap contains a portion of the image, for example when zooming.
3272961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * Another useful application is for printing where the size of the bitmap holding
3282961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * the page is too large and a client can render the page in stripes.
3292961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * </p>
3302961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * <p>
3312961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * <strong>Note: </strong> The destination bitmap format must be
3322961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * {@link Config#ARGB_8888 ARGB}.
3332961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * </p>
3342961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * <p>
3352961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * <strong>Note: </strong> The optional transformation matrix must be affine as per
33695b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav         * {@link android.graphics.Matrix#isAffine() Matrix.isAffine()}. Hence, you can specify
33795b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav         * rotation, scaling, translation but not a perspective transformation.
3382961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * </p>
3392961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         *
3402961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @param destination Destination bitmap to which to render.
3412961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @param destClip Optional clip in the bitmap bounds.
3422961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @param transform Optional transformation to apply when rendering.
3432961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @param renderMode The render mode.
3442961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         *
3452961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @see #RENDER_MODE_FOR_DISPLAY
3462961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         * @see #RENDER_MODE_FOR_PRINT
3472961769ea94f69c191a2dd785b2504666c7292d0Svetoslav         */
3482961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        public void render(@NonNull Bitmap destination, @Nullable Rect destClip,
3492961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                           @Nullable Matrix transform, @RenderMode int renderMode) {
3502961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            if (destination.getConfig() != Config.ARGB_8888) {
3512961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                throw new IllegalArgumentException("Unsupported pixel format");
3522961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            }
3532961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3542961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            if (destClip != null) {
3552961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                if (destClip.left < 0 || destClip.top < 0
3562961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                        || destClip.right > destination.getWidth()
3572961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                        || destClip.bottom > destination.getHeight()) {
3582961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                    throw new IllegalArgumentException("destBounds not in destination");
3592961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                }
3602961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            }
3612961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3622961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            if (transform != null && !transform.isAffine()) {
3632961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                 throw new IllegalArgumentException("transform not affine");
3642961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            }
3652961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3662961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            if (renderMode != RENDER_MODE_FOR_PRINT && renderMode != RENDER_MODE_FOR_DISPLAY) {
3672961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                throw new IllegalArgumentException("Unsupported render mode");
3682961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            }
3692961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3702961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            if (renderMode == RENDER_MODE_FOR_PRINT && renderMode == RENDER_MODE_FOR_DISPLAY) {
3712961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                throw new IllegalArgumentException("Only single render mode supported");
3722961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            }
3732961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3742961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            final int contentLeft = (destClip != null) ? destClip.left : 0;
3752961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            final int contentTop = (destClip != null) ? destClip.top : 0;
3762961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            final int contentRight = (destClip != null) ? destClip.right
3772961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                    : destination.getWidth();
3782961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            final int contentBottom = (destClip != null) ? destClip.bottom
3792961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                    : destination.getHeight();
3802961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3812961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            final long transformPtr = (transform != null) ? transform.native_instance : 0;
3822961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
3832961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            nativeRenderPage(mNativeDocument, mNativePage, destination.mNativeBitmap, contentLeft,
3842961769ea94f69c191a2dd785b2504666c7292d0Svetoslav                    contentTop, contentRight, contentBottom, transformPtr, renderMode);
3852961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
3862961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
38795b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        /**
38895b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav         * Closes this page.
38995b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav         *
39095b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav         * @see android.graphics.pdf.PdfRenderer#openPage(int)
39195b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav         */
39295b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        @Override
39395b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        public void close() {
39495b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            throwIfClosed();
39595b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            doClose();
39695b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        }
39795b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav
39895b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        @Override
39995b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        protected void finalize() throws Throwable {
40095b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            try {
40195b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav                mCloseGuard.warnIfOpen();
40295b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav                if (mNativePage != 0) {
40395b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav                    doClose();
40495b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav                }
40595b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            } finally {
40695b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav                super.finalize();
40795b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            }
40895b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        }
40995b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav
41095b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        private void doClose() {
4112961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            nativeClosePage(mNativePage);
4122961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            mNativePage = 0;
41395b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            mCloseGuard.close();
414525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mCurrentPage = null;
41595b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        }
41695b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav
41795b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav        private void throwIfClosed() {
41895b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            if (mNativePage == 0) {
41995b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav                throw new IllegalStateException("Already closed");
42095b6fd8b7af276069cbc415da3499d5ab4873c32Svetoslav            }
4212961769ea94f69c191a2dd785b2504666c7292d0Svetoslav        }
4222961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    }
4232961769ea94f69c191a2dd785b2504666c7292d0Svetoslav
4242961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private static native long nativeCreate(int fd, long size);
4252961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private static native void nativeClose(long documentPtr);
4262961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private static native int nativeGetPageCount(long documentPtr);
4272961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private static native boolean nativeScaleForPrinting(long documentPtr);
4282961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private static native void nativeRenderPage(long documentPtr, long pagePtr, long destPtr,
4292961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            int destLeft, int destTop, int destRight, int destBottom, long matrixPtr, int renderMode);
4302961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private static native long nativeOpenPageAndGetSize(long documentPtr, int pageIndex,
4312961769ea94f69c191a2dd785b2504666c7292d0Svetoslav            Point outSize);
4322961769ea94f69c191a2dd785b2504666c7292d0Svetoslav    private static native void nativeClosePage(long pagePtr);
4332961769ea94f69c191a2dd785b2504666c7292d0Svetoslav}
434