113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov/* 213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * Copyright (C) 2014 The Android Open Source Project 313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * 413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * Licensed under the Apache License, Version 2.0 (the "License"); 513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * you may not use this file except in compliance with the License. 613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * You may obtain a copy of the License at 713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * 813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * http://www.apache.org/licenses/LICENSE-2.0 913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * 1013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * Unless required by applicable law or agreed to in writing, software 1113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * distributed under the License is distributed on an "AS IS" BASIS, 1213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * See the License for the specific language governing permissions and 1413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov * limitations under the License. 1513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov */ 1613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 1713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovpackage com.android.printspooler.renderer; 1813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 1913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.app.Service; 2013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.content.Intent; 2113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.content.res.Configuration; 2213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.graphics.Bitmap; 236f249835a4ff9e7e7e3ca0190b7ecf72e689656dSvetoslavimport android.graphics.Color; 2413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.graphics.Matrix; 2513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.graphics.Rect; 2662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavimport android.graphics.pdf.PdfEditor; 2713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.graphics.pdf.PdfRenderer; 2813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.os.IBinder; 2913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.os.ParcelFileDescriptor; 3013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.os.RemoteException; 3113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.print.PageRange; 3213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.print.PrintAttributes; 3313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.print.PrintAttributes.Margins; 3413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.util.Log; 3513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport android.view.View; 3662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavimport com.android.printspooler.util.PageRangeUtils; 3713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport libcore.io.IoUtils; 38df6444931b030d3cdd9769e23f16f0a16fe9c654Svet Ganovimport com.android.printspooler.util.BitmapSerializeUtils; 3913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganovimport java.io.IOException; 4013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 4113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov/** 4262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav * Service for manipulation of PDF documents in an isolated process. 4313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov */ 4462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslavpublic final class PdfManipulationService extends Service { 4562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav public static final String ACTION_GET_RENDERER = 4662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav "com.android.printspooler.renderer.ACTION_GET_RENDERER"; 4762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav public static final String ACTION_GET_EDITOR = 4862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav "com.android.printspooler.renderer.ACTION_GET_EDITOR"; 4962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 50fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov public static final int ERROR_MALFORMED_PDF_FILE = -2; 51fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov 52fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov public static final int ERROR_SECURE_PDF_FILE = -3; 53d23bfa9d42c477970189a96d4562d627d609e604Svetoslav 5462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav private static final String LOG_TAG = "PdfManipulationService"; 5513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private static final boolean DEBUG = false; 5613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 5713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private static final int MILS_PER_INCH = 1000; 5813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private static final int POINTS_IN_INCH = 72; 5913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 6013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov @Override 6113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov public IBinder onBind(Intent intent) { 6262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav String action = intent.getAction(); 6362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav switch (action) { 6462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav case ACTION_GET_RENDERER: { 6562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav return new PdfRendererImpl(); 6662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 6762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav case ACTION_GET_EDITOR: { 6862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav return new PdfEditorImpl(); 6962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 7062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav default: { 7162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throw new IllegalArgumentException("Invalid intent action:" + action); 7262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 7362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 7413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 7513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 7613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private final class PdfRendererImpl extends IPdfRenderer.Stub { 7713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private final Object mLock = new Object(); 7813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 7913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private Bitmap mBitmap; 8013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private PdfRenderer mRenderer; 8113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 8213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov @Override 8313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov public int openDocument(ParcelFileDescriptor source) throws RemoteException { 8413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov synchronized (mLock) { 8513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov try { 8662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throwIfOpened(); 8762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav if (DEBUG) { 8862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.i(LOG_TAG, "openDocument()"); 8962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 9013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov mRenderer = new PdfRenderer(source); 9113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov return mRenderer.getPageCount(); 92bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } catch (IOException | IllegalStateException e) { 9362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav IoUtils.closeQuietly(source); 9462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.e(LOG_TAG, "Cannot open file", e); 95fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov return ERROR_MALFORMED_PDF_FILE; 96fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov } catch (SecurityException e) { 97fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov IoUtils.closeQuietly(source); 98fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov Log.e(LOG_TAG, "Cannot open file", e); 99fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov return ERROR_SECURE_PDF_FILE; 10013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 10113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 10213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 10313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 10413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov @Override 10513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov public void renderPage(int pageIndex, int bitmapWidth, int bitmapHeight, 10613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov PrintAttributes attributes, ParcelFileDescriptor destination) { 10713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov synchronized (mLock) { 10813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov try { 10913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov throwIfNotOpened(); 11013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 111066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann try (PdfRenderer.Page page = mRenderer.openPage(pageIndex)) { 112066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final int srcWidthPts = page.getWidth(); 113066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final int srcHeightPts = page.getHeight(); 11413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 115066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final int dstWidthPts = pointsFromMils( 116066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann attributes.getMediaSize().getWidthMils()); 117066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final int dstHeightPts = pointsFromMils( 118066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann attributes.getMediaSize().getHeightMils()); 11913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 120066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final boolean scaleContent = mRenderer.shouldScaleForPrinting(); 121066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final boolean contentLandscape = !attributes.getMediaSize().isPortrait(); 12213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 123066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final float displayScale; 124066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann Matrix matrix = new Matrix(); 12513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 126066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann if (scaleContent) { 127066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann displayScale = Math.min((float) bitmapWidth / srcWidthPts, 128066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann (float) bitmapHeight / srcHeightPts); 12913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } else { 130066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann if (contentLandscape) { 131066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann displayScale = (float) bitmapHeight / dstHeightPts; 132066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann } else { 133066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann displayScale = (float) bitmapWidth / dstWidthPts; 134066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann } 13513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 136066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann matrix.postScale(displayScale, displayScale); 13713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 138066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann Configuration configuration = PdfManipulationService.this.getResources() 139066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann .getConfiguration(); 140066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann if (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { 141066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann matrix.postTranslate(bitmapWidth - srcWidthPts * displayScale, 0); 142066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann } 14313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 144066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann Margins minMargins = attributes.getMinMargins(); 145066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils()); 146066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final int paddingTopPts = pointsFromMils(minMargins.getTopMils()); 147066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final int paddingRightPts = pointsFromMils(minMargins.getRightMils()); 148066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils()); 14913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 150066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann Rect clip = new Rect(); 151066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann clip.left = (int) (paddingLeftPts * displayScale); 152066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann clip.top = (int) (paddingTopPts * displayScale); 153066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann clip.right = (int) (bitmapWidth - paddingRightPts * displayScale); 154066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann clip.bottom = (int) (bitmapHeight - paddingBottomPts * displayScale); 15513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 156066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann if (DEBUG) { 157066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann Log.i(LOG_TAG, "Rendering page:" + pageIndex); 158066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann } 15913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 160066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann Bitmap bitmap = getBitmapForSize(bitmapWidth, bitmapHeight); 161066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann page.render(bitmap, clip, matrix, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY); 16213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 163066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann BitmapSerializeUtils.writeBitmapPixels(bitmap, destination); 164066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann } 165066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann } catch (Throwable e) { 166066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann Log.e(LOG_TAG, "Cannot render page", e); 16713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 168066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann // The error is propagated to the caller when it tries to read the bitmap and 169066bf81b983ce23b91d19b85b7c37a61fba7a9a6Philip P. Moltmann // the pipe is closed prematurely 17013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } finally { 17113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov IoUtils.closeQuietly(destination); 17213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 17313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 17413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 17513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 17613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov @Override 17713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov public void closeDocument() { 17813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov synchronized (mLock) { 17913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov throwIfNotOpened(); 18013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov if (DEBUG) { 18162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.i(LOG_TAG, "closeDocument()"); 18213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 18313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov mRenderer.close(); 18413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov mRenderer = null; 18513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 18613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 18713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 18813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private Bitmap getBitmapForSize(int width, int height) { 18913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov if (mBitmap != null) { 19013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov if (mBitmap.getWidth() == width && mBitmap.getHeight() == height) { 1916f249835a4ff9e7e7e3ca0190b7ecf72e689656dSvetoslav mBitmap.eraseColor(Color.WHITE); 19213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov return mBitmap; 19313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 19413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov mBitmap.recycle(); 19513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 19613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 1976f249835a4ff9e7e7e3ca0190b7ecf72e689656dSvetoslav mBitmap.eraseColor(Color.WHITE); 19813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov return mBitmap; 19913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 20013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 20113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private void throwIfOpened() { 20213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov if (mRenderer != null) { 20313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov throw new IllegalStateException("Already opened"); 20413f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 20513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 20613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 20713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private void throwIfNotOpened() { 20813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov if (mRenderer == null) { 20913f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov throw new IllegalStateException("Not opened"); 21013f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 21113f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 21213f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 21313f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov 21462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav private final class PdfEditorImpl extends IPdfEditor.Stub { 21562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav private final Object mLock = new Object(); 21662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 21762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav private PdfEditor mEditor; 21862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 21962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav @Override 22062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav public int openDocument(ParcelFileDescriptor source) throws RemoteException { 22162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav synchronized (mLock) { 22262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav try { 22362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throwIfOpened(); 22462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav if (DEBUG) { 22562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.i(LOG_TAG, "openDocument()"); 22662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 22762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav mEditor = new PdfEditor(source); 22862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav return mEditor.getPageCount(); 229bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } catch (IOException | IllegalStateException e) { 23062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav IoUtils.closeQuietly(source); 23162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.e(LOG_TAG, "Cannot open file", e); 23262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throw new RemoteException(e.toString()); 23362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 23462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 23562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 23662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 23762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav @Override 23862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav public void removePages(PageRange[] ranges) { 23962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav synchronized (mLock) { 24062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throwIfNotOpened(); 24162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav if (DEBUG) { 24262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.i(LOG_TAG, "removePages()"); 24362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 24462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 24562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav ranges = PageRangeUtils.normalize(ranges); 24662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 2479e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann int lastPageIdx = mEditor.getPageCount() - 1; 2489e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann 24962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav final int rangeCount = ranges.length; 25062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav for (int i = rangeCount - 1; i >= 0; i--) { 25162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav PageRange range = ranges[i]; 2529e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann 2539e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann // Ignore removal of pages that are outside the document 2549e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann if (range.getEnd() > lastPageIdx) { 2559e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann if (range.getStart() > lastPageIdx) { 2569e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann continue; 2579e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann } 2589e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann range = new PageRange(range.getStart(), lastPageIdx); 2599e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann } 2609e4bbc60a9e95f624e577997259fbd6500dac15dPhilip P. Moltmann 26162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav for (int j = range.getEnd(); j >= range.getStart(); j--) { 26262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav mEditor.removePage(j); 26362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 26462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 26562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 26662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 26762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 26862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav @Override 269bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav public void applyPrintAttributes(PrintAttributes attributes) { 270bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav synchronized (mLock) { 271bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav throwIfNotOpened(); 272bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav if (DEBUG) { 273bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav Log.i(LOG_TAG, "applyPrintAttributes()"); 274bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } 275bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 276bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav Rect mediaBox = new Rect(); 277bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav Rect cropBox = new Rect(); 278bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav Matrix transform = new Matrix(); 279bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 280bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final boolean layoutDirectionRtl = getResources().getConfiguration() 281bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 282bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 283bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav // We do not want to rotate the media box, so take into account orientation. 284cd7b1b7cdf61298966ed7df6621d077afbf81aedPhilip P. Moltmann final int dstWidthPts = pointsFromMils(attributes.getMediaSize().getWidthMils()); 285cd7b1b7cdf61298966ed7df6621d077afbf81aedPhilip P. Moltmann final int dstHeightPts = pointsFromMils(attributes.getMediaSize().getHeightMils()); 286bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 287bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final boolean scaleForPrinting = mEditor.shouldScaleForPrinting(); 288bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 289bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final int pageCount = mEditor.getPageCount(); 290bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav for (int i = 0; i < pageCount; i++) { 291bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav if (!mEditor.getPageMediaBox(i, mediaBox)) { 292bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav Log.e(LOG_TAG, "Malformed PDF file"); 293bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav return; 294bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } 295bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 296bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final int srcWidthPts = mediaBox.width(); 297bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final int srcHeightPts = mediaBox.height(); 298bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 299bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav // Update the media box with the desired size. 300bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav mediaBox.right = dstWidthPts; 301bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav mediaBox.bottom = dstHeightPts; 302bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav mEditor.setPageMediaBox(i, mediaBox); 303bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 304bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav // Make sure content is top-left after media box resize. 305bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav transform.setTranslate(0, srcHeightPts - dstHeightPts); 306bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 307bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav // Scale the content if document allows it. 308bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final float scale; 309bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav if (scaleForPrinting) { 310cd7b1b7cdf61298966ed7df6621d077afbf81aedPhilip P. Moltmann scale = Math.min((float) dstWidthPts / srcWidthPts, 311cd7b1b7cdf61298966ed7df6621d077afbf81aedPhilip P. Moltmann (float) dstHeightPts / srcHeightPts); 312cd7b1b7cdf61298966ed7df6621d077afbf81aedPhilip P. Moltmann transform.postScale(scale, scale); 313bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } else { 314bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav scale = 1.0f; 315bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } 316bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 317bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav // Update the crop box relatively to the media box change, if needed. 318bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav if (mEditor.getPageCropBox(i, cropBox)) { 319bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav cropBox.left = (int) (cropBox.left * scale + 0.5f); 320bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav cropBox.top = (int) (cropBox.top * scale + 0.5f); 321bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav cropBox.right = (int) (cropBox.right * scale + 0.5f); 322bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav cropBox.bottom = (int) (cropBox.bottom * scale + 0.5f); 323bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav cropBox.intersect(mediaBox); 324bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav mEditor.setPageCropBox(i, cropBox); 325bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } 326bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 327bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav // If in RTL mode put the content in the logical top-right corner. 328bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav if (layoutDirectionRtl) { 329cd7b1b7cdf61298966ed7df6621d077afbf81aedPhilip P. Moltmann final float dx = dstWidthPts - (int) (srcWidthPts * scale + 0.5f); 330cd7b1b7cdf61298966ed7df6621d077afbf81aedPhilip P. Moltmann final float dy = 0; 331bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav transform.postTranslate(dx, dy); 332bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } 333bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 334bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav // Adjust the physical margins if needed. 335bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav Margins minMargins = attributes.getMinMargins(); 336bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils()); 337bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final int paddingTopPts = pointsFromMils(minMargins.getTopMils()); 338bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final int paddingRightPts = pointsFromMils(minMargins.getRightMils()); 339bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils()); 340bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 341bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav Rect clip = new Rect(mediaBox); 342bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav clip.left += paddingLeftPts; 343bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav clip.top += paddingTopPts; 344bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav clip.right -= paddingRightPts; 345bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav clip.bottom -= paddingBottomPts; 346bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 347bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav // Apply the accumulated transforms. 348bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav mEditor.setTransformAndClip(i, transform, clip); 349bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } 350bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } 351bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav } 352bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav 353bec22beb99b279d381f720d761ca75fe3e7414dcSvetoslav @Override 35462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav public void write(ParcelFileDescriptor destination) throws RemoteException { 35562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav synchronized (mLock) { 35662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav try { 35762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throwIfNotOpened(); 35862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav if (DEBUG) { 35962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.i(LOG_TAG, "write()"); 36062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 36162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav mEditor.write(destination); 36262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } catch (IOException | IllegalStateException e) { 36362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav IoUtils.closeQuietly(destination); 36462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.e(LOG_TAG, "Error writing PDF to file.", e); 36562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throw new RemoteException(e.toString()); 36662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 36762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 36862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 36962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 37062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav @Override 37162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav public void closeDocument() { 37262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav synchronized (mLock) { 37362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throwIfNotOpened(); 37462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav if (DEBUG) { 37562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav Log.i(LOG_TAG, "closeDocument()"); 37662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 37762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav mEditor.close(); 37862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav mEditor = null; 37962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 38062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 38162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 38262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav private void throwIfOpened() { 38362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav if (mEditor != null) { 38462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throw new IllegalStateException("Already opened"); 38562ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 38662ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 38762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 38862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav private void throwIfNotOpened() { 38962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav if (mEditor == null) { 39062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav throw new IllegalStateException("Not opened"); 39162ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 39262ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 39362ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav } 39462ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav 39513f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov private static int pointsFromMils(int mils) { 39613f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov return (int) (((float) mils / MILS_PER_INCH) * POINTS_IN_INCH); 39713f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov } 39813f542cabd635c55ade5442764cc4a3d2f7880eaSvet Ganov} 399