blob: 0d57d5cd23624e24b86f5fb2abfa5e6674c09b0a [file] [log] [blame]
Svet Ganov13f542ca2014-08-29 15:35:49 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.printspooler.renderer;
18
19import android.app.Service;
20import android.content.Intent;
21import android.content.res.Configuration;
22import android.graphics.Bitmap;
23import android.graphics.Matrix;
24import android.graphics.Rect;
25import android.graphics.pdf.PdfRenderer;
26import android.os.IBinder;
27import android.os.ParcelFileDescriptor;
28import android.os.RemoteException;
29import android.print.PageRange;
30import android.print.PrintAttributes;
31import android.print.PrintAttributes.Margins;
32import android.util.Log;
33import android.view.View;
34import libcore.io.IoUtils;
Svet Ganovdf644492014-08-29 15:35:49 -070035import com.android.printspooler.util.BitmapSerializeUtils;
Svet Ganov13f542ca2014-08-29 15:35:49 -070036import java.io.IOException;
37
38/**
39 * Service for rendering PDF documents in an isolated process.
40 */
41public final class PdfRendererService extends Service {
42 private static final String LOG_TAG = "PdfRendererService";
43 private static final boolean DEBUG = false;
44
45 private static final int MILS_PER_INCH = 1000;
46 private static final int POINTS_IN_INCH = 72;
47
48 @Override
49 public IBinder onBind(Intent intent) {
50 return new PdfRendererImpl();
51 }
52
53 private final class PdfRendererImpl extends IPdfRenderer.Stub {
54 private final Object mLock = new Object();
55
56 private Bitmap mBitmap;
57 private PdfRenderer mRenderer;
58
59 @Override
60 public int openDocument(ParcelFileDescriptor source) throws RemoteException {
61 synchronized (mLock) {
62 throwIfOpened();
63 if (DEBUG) {
64 Log.i(LOG_TAG, "openDocument()");
65 }
66 try {
67 mRenderer = new PdfRenderer(source);
68 return mRenderer.getPageCount();
69 } catch (IOException ioe) {
70 throw new RemoteException("Cannot open file");
71 }
72 }
73 }
74
75 @Override
76 public void renderPage(int pageIndex, int bitmapWidth, int bitmapHeight,
77 PrintAttributes attributes, ParcelFileDescriptor destination) {
Svet Ganov13f542ca2014-08-29 15:35:49 -070078 synchronized (mLock) {
79 try {
80 throwIfNotOpened();
81
82 PdfRenderer.Page page = mRenderer.openPage(pageIndex);
83
84 final int srcWidthPts = page.getWidth();
85 final int srcHeightPts = page.getHeight();
86
87 final int dstWidthPts = pointsFromMils(
88 attributes.getMediaSize().getWidthMils());
89 final int dstHeightPts = pointsFromMils(
90 attributes.getMediaSize().getHeightMils());
91
92 final boolean scaleContent = mRenderer.shouldScaleForPrinting();
93 final boolean contentLandscape = !attributes.getMediaSize().isPortrait();
94
95 final float displayScale;
96 Matrix matrix = new Matrix();
97
98 if (scaleContent) {
99 displayScale = Math.min((float) bitmapWidth / srcWidthPts,
100 (float) bitmapHeight / srcHeightPts);
101 } else {
102 if (contentLandscape) {
103 displayScale = (float) bitmapHeight / dstHeightPts;
104 } else {
105 displayScale = (float) bitmapWidth / dstWidthPts;
106 }
107 }
108 matrix.postScale(displayScale, displayScale);
109
110 Configuration configuration = PdfRendererService.this.getResources()
111 .getConfiguration();
112 if (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
113 matrix.postTranslate(bitmapWidth - srcWidthPts * displayScale, 0);
114 }
115
116 Margins minMargins = attributes.getMinMargins();
117 final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils());
118 final int paddingTopPts = pointsFromMils(minMargins.getTopMils());
119 final int paddingRightPts = pointsFromMils(minMargins.getRightMils());
120 final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils());
121
122 Rect clip = new Rect();
123 clip.left = (int) (paddingLeftPts * displayScale);
124 clip.top = (int) (paddingTopPts * displayScale);
125 clip.right = (int) (bitmapWidth - paddingRightPts * displayScale);
126 clip.bottom = (int) (bitmapHeight - paddingBottomPts * displayScale);
127
128 if (DEBUG) {
129 Log.i(LOG_TAG, "Rendering page:" + pageIndex);
130 }
131
132 Bitmap bitmap = getBitmapForSize(bitmapWidth, bitmapHeight);
133 page.render(bitmap, clip, matrix, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
134
135 page.close();
136
Svet Ganovdf644492014-08-29 15:35:49 -0700137 BitmapSerializeUtils.writeBitmapPixels(bitmap, destination);
Svet Ganov13f542ca2014-08-29 15:35:49 -0700138 } finally {
Svet Ganov13f542ca2014-08-29 15:35:49 -0700139 IoUtils.closeQuietly(destination);
140 }
141 }
142 }
143
144 @Override
145 public void closeDocument() {
146 synchronized (mLock) {
147 throwIfNotOpened();
148 if (DEBUG) {
149 Log.i(LOG_TAG, "openDocument()");
150 }
151 mRenderer.close();
152 mRenderer = null;
153 }
154 }
155
156 @Override
157 public void writePages(PageRange[] pages) {
158 synchronized (mLock) {
159 throwIfNotOpened();
160 if (DEBUG) {
161 Log.i(LOG_TAG, "writePages()");
162 }
163 // TODO: Implement dropping undesired pages.
164 }
165 }
166
167 private Bitmap getBitmapForSize(int width, int height) {
168 if (mBitmap != null) {
169 if (mBitmap.getWidth() == width && mBitmap.getHeight() == height) {
170 return mBitmap;
171 }
172 mBitmap.recycle();
173 }
174 mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
175 return mBitmap;
176 }
177
178 private void throwIfOpened() {
179 if (mRenderer != null) {
180 throw new IllegalStateException("Already opened");
181 }
182 }
183
184 private void throwIfNotOpened() {
185 if (mRenderer == null) {
186 throw new IllegalStateException("Not opened");
187 }
188 }
189 }
190
191 private static int pointsFromMils(int mils) {
192 return (int) (((float) mils / MILS_PER_INCH) * POINTS_IN_INCH);
193 }
194}