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