From bec22beb99b279d381f720d761ca75fe3e7414dc Mon Sep 17 00:00:00 2001 From: Svetoslav Date: Thu, 25 Sep 2014 13:03:20 -0700 Subject: Save to a PDF file should look like print preview. When rendering a PDF file for print preview we take into account the selected print options such as paper size, orientation, etc without modifying the document. To print we send the doc in its original form and the print options so the print service can apply the necessary transforms in addition to the optional custom options it supports. When saving to PDF we have to actually change the document as we act as a print service. bug:13545980 Change-Id: Icdcecf962bec6ff742cc6015df5af9d9086ce760 --- .../android/printspooler/renderer/IPdfEditor.aidl | 2 + .../renderer/PdfManipulationService.java | 109 ++++++++++++++++++++- .../com/android/printspooler/ui/PrintActivity.java | 38 ++++--- 3 files changed, 134 insertions(+), 15 deletions(-) (limited to 'packages/PrintSpooler/src/com/android') diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl b/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl index b450ccb..01cabe1 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl +++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/IPdfEditor.aidl @@ -18,6 +18,7 @@ package com.android.printspooler.renderer; import android.os.ParcelFileDescriptor; import android.print.PageRange; +import android.print.PrintAttributes; /** * Interface for communication with a remote pdf editor. @@ -25,6 +26,7 @@ import android.print.PageRange; interface IPdfEditor { int openDocument(in ParcelFileDescriptor source); void removePages(in PageRange[] pages); + void applyPrintAttributes(in PrintAttributes attributes); void write(in ParcelFileDescriptor destination); void closeDocument(); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java index 00e5051..0462e4d 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java @@ -87,7 +87,7 @@ public final class PdfManipulationService extends Service { } mRenderer = new PdfRenderer(source); return mRenderer.getPageCount(); - } catch (IOException|IllegalStateException e) { + } catch (IOException | IllegalStateException e) { IoUtils.closeQuietly(source); Log.e(LOG_TAG, "Cannot open file", e); return MALFORMED_PDF_FILE_ERROR; @@ -217,7 +217,7 @@ public final class PdfManipulationService extends Service { } mEditor = new PdfEditor(source); return mEditor.getPageCount(); - } catch (IOException|IllegalStateException e) { + } catch (IOException | IllegalStateException e) { IoUtils.closeQuietly(source); Log.e(LOG_TAG, "Cannot open file", e); throw new RemoteException(e.toString()); @@ -246,6 +246,111 @@ public final class PdfManipulationService extends Service { } @Override + public void applyPrintAttributes(PrintAttributes attributes) { + synchronized (mLock) { + throwIfNotOpened(); + if (DEBUG) { + Log.i(LOG_TAG, "applyPrintAttributes()"); + } + + Rect mediaBox = new Rect(); + Rect cropBox = new Rect(); + Matrix transform = new Matrix(); + + final boolean contentPortrait = attributes.getMediaSize().isPortrait(); + + final boolean layoutDirectionRtl = getResources().getConfiguration() + .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + + // We do not want to rotate the media box, so take into account orientation. + final int dstWidthPts = contentPortrait + ? pointsFromMils(attributes.getMediaSize().getWidthMils()) + : pointsFromMils(attributes.getMediaSize().getHeightMils()); + final int dstHeightPts = contentPortrait + ? pointsFromMils(attributes.getMediaSize().getHeightMils()) + : pointsFromMils(attributes.getMediaSize().getWidthMils()); + + final boolean scaleForPrinting = mEditor.shouldScaleForPrinting(); + + final int pageCount = mEditor.getPageCount(); + for (int i = 0; i < pageCount; i++) { + if (!mEditor.getPageMediaBox(i, mediaBox)) { + Log.e(LOG_TAG, "Malformed PDF file"); + return; + } + + final int srcWidthPts = mediaBox.width(); + final int srcHeightPts = mediaBox.height(); + + // Update the media box with the desired size. + mediaBox.right = dstWidthPts; + mediaBox.bottom = dstHeightPts; + mEditor.setPageMediaBox(i, mediaBox); + + // Make sure content is top-left after media box resize. + transform.setTranslate(0, srcHeightPts - dstHeightPts); + + // Rotate the content if in landscape. + if (!contentPortrait) { + transform.postRotate(270); + transform.postTranslate(0, dstHeightPts); + } + + // Scale the content if document allows it. + final float scale; + if (scaleForPrinting) { + if (contentPortrait) { + scale = Math.min((float) dstWidthPts / srcWidthPts, + (float) dstHeightPts / srcHeightPts); + transform.postScale(scale, scale); + } else { + scale = Math.min((float) dstWidthPts / srcHeightPts, + (float) dstHeightPts / srcWidthPts); + transform.postScale(scale, scale, mediaBox.left, mediaBox.bottom); + } + } else { + scale = 1.0f; + } + + // Update the crop box relatively to the media box change, if needed. + if (mEditor.getPageCropBox(i, cropBox)) { + cropBox.left = (int) (cropBox.left * scale + 0.5f); + cropBox.top = (int) (cropBox.top * scale + 0.5f); + cropBox.right = (int) (cropBox.right * scale + 0.5f); + cropBox.bottom = (int) (cropBox.bottom * scale + 0.5f); + cropBox.intersect(mediaBox); + mEditor.setPageCropBox(i, cropBox); + } + + // If in RTL mode put the content in the logical top-right corner. + if (layoutDirectionRtl) { + final float dx = contentPortrait + ? dstWidthPts - (int) (srcWidthPts * scale + 0.5f) : 0; + final float dy = contentPortrait + ? 0 : - (dstHeightPts - (int) (srcWidthPts * scale + 0.5f)); + transform.postTranslate(dx, dy); + } + + // Adjust the physical margins if needed. + Margins minMargins = attributes.getMinMargins(); + final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils()); + final int paddingTopPts = pointsFromMils(minMargins.getTopMils()); + final int paddingRightPts = pointsFromMils(minMargins.getRightMils()); + final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils()); + + Rect clip = new Rect(mediaBox); + clip.left += paddingLeftPts; + clip.top += paddingTopPts; + clip.right -= paddingRightPts; + clip.bottom -= paddingBottomPts; + + // Apply the accumulated transforms. + mEditor.setTransformAndClip(i, transform, clip); + } + } + } + + @Override public void write(ParcelFileDescriptor destination) throws RemoteException { synchronized (mLock) { try { diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 21c8b83..0981c3f 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -592,7 +592,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mDestinationSpinner.post(new Runnable() { @Override public void run() { - shredPagesAndFinish(uri); + transformDocumentAndFinish(uri); } }); } else if (resultCode == RESULT_CANCELED) { @@ -922,7 +922,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) { startCreateDocumentActivity(); } else { - shredPagesAndFinish(null); + transformDocumentAndFinish(null); } } @@ -1597,8 +1597,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat return true; } - private void shredPagesAndFinish(final Uri writeToUri) { - new PageShredder(this, mPrintJob, mFileProvider, new Runnable() { + private void transformDocumentAndFinish(final Uri writeToUri) { + // If saving to PDF, apply the attibutes as we are acting as a print service. + PrintAttributes attributes = mDestinationSpinnerAdapter.getPdfPrinter() == mCurrentPrinter + ? mPrintJob.getAttributes() : null; + new DocumentTransformer(this, mPrintJob, mFileProvider, attributes, new Runnable() { @Override public void run() { if (writeToUri != null) { @@ -1606,7 +1609,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } doFinish(); } - }).shred(); + }).transform(); } private void doFinish() { @@ -2335,7 +2338,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } } - private static final class PageShredder implements ServiceConnection { + private static final class DocumentTransformer implements ServiceConnection { private static final String TEMP_FILE_PREFIX = "print_job"; private static final String TEMP_FILE_EXTENSION = ".pdf"; @@ -2347,20 +2350,24 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private final PageRange[] mPagesToShred; + private final PrintAttributes mAttributesToApply; + private final Runnable mCallback; - public PageShredder(Context context, PrintJobInfo printJob, - MutexFileProvider fileProvider, Runnable callback) { + public DocumentTransformer(Context context, PrintJobInfo printJob, + MutexFileProvider fileProvider, PrintAttributes attributes, + Runnable callback) { mContext = context; mPrintJob = printJob; mFileProvider = fileProvider; mCallback = callback; mPagesToShred = computePagesToShred(mPrintJob); + mAttributesToApply = attributes; } - public void shred() { + public void transform() { // If we have only the pages we want, done. - if (mPagesToShred.length <= 0) { + if (mPagesToShred.length <= 0 && mAttributesToApply == null) { mCallback.run(); return; } @@ -2382,14 +2389,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // final and this code is the last one to touch // them as shredding is the very last step, so the // UI is not interactive at this point. - shredPages(editor); + doTransform(editor); updatePrintJob(); return null; } @Override protected void onPostExecute(Void aVoid) { - mContext.unbindService(PageShredder.this); + mContext.unbindService(DocumentTransformer.this); mCallback.run(); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -2400,7 +2407,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat /* do nothing */ } - private void shredPages(IPdfEditor editor) { + private void doTransform(IPdfEditor editor) { File tempFile = null; ParcelFileDescriptor src = null; ParcelFileDescriptor dst = null; @@ -2419,6 +2426,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // Drop the pages. editor.removePages(mPagesToShred); + // Apply print attributes if needed. + if (mAttributesToApply != null) { + editor.applyPrintAttributes(mAttributesToApply); + } + // Write the modified PDF to a temp file. tempFile = File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_EXTENSION, mContext.getCacheDir()); -- cgit v1.1