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