/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v4.print; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.pdf.PdfDocument.Page; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; import android.print.PageRange; import android.print.PrintAttributes; import android.print.PrintDocumentAdapter; import android.print.PrintDocumentInfo; import android.print.PrintManager; import android.print.pdf.PrintedPdfDocument; import android.util.Log; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; /** * Kitkat specific PrintManager API implementation. */ class PrintHelperKitkat { private static final String LOG_TAG = "PrintHelperKitkat"; // will be <= 300 dpi on A4 (8.3×11.7) paper (worst case of 150 dpi) private final static int MAX_PRINT_SIZE = 3500; final Context mContext; BitmapFactory.Options mDecodeOptions = null; private final Object mLock = new Object(); /** * image will be scaled but leave white space */ public static final int SCALE_MODE_FIT = 1; /** * image will fill the paper and be cropped (default) */ public static final int SCALE_MODE_FILL = 2; /** * select landscape (default) */ public static final int ORIENTATION_LANDSCAPE = 1; /** * select portrait */ public static final int ORIENTATION_PORTRAIT = 2; /** * this is a black and white image */ public static final int COLOR_MODE_MONOCHROME = 1; /** * this is a color image (default) */ public static final int COLOR_MODE_COLOR = 2; public interface OnPrintFinishCallback { public void onFinish(); } int mScaleMode = SCALE_MODE_FILL; int mColorMode = COLOR_MODE_COLOR; int mOrientation = ORIENTATION_LANDSCAPE; PrintHelperKitkat(Context context) { mContext = context; } /** * Selects whether the image will fill the paper and be cropped *
* {@link #SCALE_MODE_FIT} * or whether the image will be scaled but leave white space * {@link #SCALE_MODE_FILL}. * * @param scaleMode {@link #SCALE_MODE_FIT} or * {@link #SCALE_MODE_FILL} */ public void setScaleMode(int scaleMode) { mScaleMode = scaleMode; } /** * Returns the scale mode with which the image will fill the paper. * * @return The scale Mode: {@link #SCALE_MODE_FIT} or * {@link #SCALE_MODE_FILL} */ public int getScaleMode() { return mScaleMode; } /** * Sets whether the image will be printed in color (default) * {@link #COLOR_MODE_COLOR} or in back and white * {@link #COLOR_MODE_MONOCHROME}. * * @param colorMode The color mode which is one of * {@link #COLOR_MODE_COLOR} and {@link #COLOR_MODE_MONOCHROME}. */ public void setColorMode(int colorMode) { mColorMode = colorMode; } /** * Sets whether to select landscape (default), {@link #ORIENTATION_LANDSCAPE} * or portrait {@link #ORIENTATION_PORTRAIT} * @param orientation The page orientation which is one of * {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}. */ public void setOrientation(int orientation) { mOrientation = orientation; } /** * Gets the page orientation with which the image will be printed. * * @return The preferred orientation which is one of * {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT} */ public int getOrientation() { return mOrientation; } /** * Gets the color mode with which the image will be printed. * * @return The color mode which is one of {@link #COLOR_MODE_COLOR} * and {@link #COLOR_MODE_MONOCHROME}. */ public int getColorMode() { return mColorMode; } /** * Prints a bitmap. * * @param jobName The print job name. * @param bitmap The bitmap to print. * @param callback Optional callback to observe when printing is finished. */ public void printBitmap(final String jobName, final Bitmap bitmap, final OnPrintFinishCallback callback) { if (bitmap == null) { return; } final int fittingMode = mScaleMode; // grab the fitting mode at time of call PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE); PrintAttributes.MediaSize mediaSize = PrintAttributes.MediaSize.UNKNOWN_PORTRAIT; if (bitmap.getWidth() > bitmap.getHeight()) { mediaSize = PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE; } PrintAttributes attr = new PrintAttributes.Builder() .setMediaSize(mediaSize) .setColorMode(mColorMode) .build(); printManager.print(jobName, new PrintDocumentAdapter() { private PrintAttributes mAttributes; @Override public void onLayout(PrintAttributes oldPrintAttributes, PrintAttributes newPrintAttributes, CancellationSignal cancellationSignal, LayoutResultCallback layoutResultCallback, Bundle bundle) { mAttributes = newPrintAttributes; PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName) .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO) .setPageCount(1) .build(); boolean changed = !newPrintAttributes.equals(oldPrintAttributes); layoutResultCallback.onLayoutFinished(info, changed); } @Override public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor, CancellationSignal cancellationSignal, WriteResultCallback writeResultCallback) { PrintedPdfDocument pdfDocument = new PrintedPdfDocument(mContext, mAttributes); Bitmap maybeGrayscale = convertBitmapForColorMode(bitmap, mAttributes.getColorMode()); try { Page page = pdfDocument.startPage(1); RectF content = new RectF(page.getInfo().getContentRect()); Matrix matrix = getMatrix( maybeGrayscale.getWidth(), maybeGrayscale.getHeight(), content, fittingMode); // Draw the bitmap. page.getCanvas().drawBitmap(maybeGrayscale, matrix, null); // Finish the page. pdfDocument.finishPage(page); try { // Write the document. pdfDocument.writeTo(new FileOutputStream( fileDescriptor.getFileDescriptor())); // Done. writeResultCallback.onWriteFinished( new PageRange[]{PageRange.ALL_PAGES}); } catch (IOException ioe) { // Failed. Log.e(LOG_TAG, "Error writing printed content", ioe); writeResultCallback.onWriteFailed(null); } } finally { if (pdfDocument != null) { pdfDocument.close(); } if (fileDescriptor != null) { try { fileDescriptor.close(); } catch (IOException ioe) { /* ignore */ } } // If we created a new instance for grayscaling, then recycle it here. if (maybeGrayscale != bitmap) { maybeGrayscale.recycle(); } } } @Override public void onFinish() { if (callback != null) { callback.onFinish(); } } }, attr); } /** * Calculates the transform the print an Image to fill the page * * @param imageWidth with of bitmap * @param imageHeight height of bitmap * @param content The output page dimensions * @param fittingMode The mode of fitting {@link #SCALE_MODE_FILL} vs {@link #SCALE_MODE_FIT} * @return Matrix to be used in canvas.drawBitmap(bitmap, matrix, null) call */ private Matrix getMatrix(int imageWidth, int imageHeight, RectF content, int fittingMode) { Matrix matrix = new Matrix(); // Compute and apply scale to fill the page. float scale = content.width() / imageWidth; if (fittingMode == SCALE_MODE_FILL) { scale = Math.max(scale, content.height() / imageHeight); } else { scale = Math.min(scale, content.height() / imageHeight); } matrix.postScale(scale, scale); // Center the content. final float translateX = (content.width() - imageWidth * scale) / 2; final float translateY = (content.height() - imageHeight * scale) / 2; matrix.postTranslate(translateX, translateY); return matrix; } /** * Prints an image located at the Uri. Image types supported are those of *BitmapFactory.decodeStream
(JPEG, GIF, PNG, BMP, WEBP)
*
* @param jobName The print job name.
* @param imageFile The Uri
pointing to an image to print.
* @param callback Optional callback to observe when printing is finished.
* @throws FileNotFoundException if Uri
is not pointing to a valid image.
*/
public void printBitmap(final String jobName, final Uri imageFile,
final OnPrintFinishCallback callback) throws FileNotFoundException {
final int fittingMode = mScaleMode;
PrintDocumentAdapter printDocumentAdapter = new PrintDocumentAdapter() {
private PrintAttributes mAttributes;
AsyncTask