diff --git a/api/current.txt b/api/current.txt
index b4e101eea58e0..8079acb7630d3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10371,6 +10371,37 @@ package android.graphics.drawable.shapes {
}
+package android.graphics.pdf {
+
+ public class PdfDocument {
+ ctor public PdfDocument();
+ method public void close();
+ method public void finishPage(android.graphics.pdf.PdfDocument.Page);
+ method public java.util.List
+ * This class computes the page width, page height, and content rectangle
+ * from the provided print attributes and these precomputed values can be
+ * accessed via {@link #getPageWidth()}, {@link #getPageHeight()}, and
+ * {@link #getPageContentRect()}, respectively. The {@link #startPage(int)}
+ * methods creates pages whose {@link PageInfo} is initialized with the
+ * precomputed values for width, height, and content rectangle.
+ *
+ * A typical use of the APIs looks like this:
+ *
* Note: You must close the document after you are
- * done by calling {@link #close()}
+ * done by calling {@link #close()}.
*
* After the page is created you can draw arbitrary content on the page's
* canvas which you can get by calling {@link Page#getCanvas() Page.getCanvas()}.
@@ -103,63 +117,48 @@ public final class PrintedPdfDocument {
*
* Note: Do not call this method after {@link #close()}.
+ * Also do not call this method if the last page returned by this method
+ * is not finished by calling {@link #finishPage(Page)}.
*
- * Note: Do not call this method after {@link #close()}.
- *
- * Note: Do not call this method after {@link #close()}.
- * true if
+ * Extra: mapped to a boolean value that is true if
* the current layout is for a print preview, false otherwise.
*/
- public static final String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW";
+ public static final String EXTRA_PRINT_PREVIEW = "EXTRA_PRINT_PREVIEW";
/**
* Called when printing starts. You can use this callback to allocate
@@ -112,15 +112,15 @@ public abstract class PrintDocumentAdapter {
* @param newAttributes The new print attributes.
* @param cancellationSignal Signal for observing cancel layout requests.
* @param callback Callback to inform the system for the layout result.
- * @param metadata Additional information about how layout the content.
+ * @param extras Additional information about how to layout the content.
*
* @see LayoutResultCallback
* @see CancellationSignal
- * @see #METADATA_KEY_PRINT_PREVIEW
+ * @see #EXTRA_PRINT_PREVIEW
*/
public abstract void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
CancellationSignal cancellationSignal, LayoutResultCallback callback,
- Bundle metadata);
+ Bundle extras);
/**
* Called when specific pages of the content should be written in the
diff --git a/core/java/android/print/pdf/PrintedPdfDocument.java b/core/java/android/print/pdf/PrintedPdfDocument.java
index 1fd4646548cb6..2d8aafa1a573a 100644
--- a/core/java/android/print/pdf/PrintedPdfDocument.java
+++ b/core/java/android/print/pdf/PrintedPdfDocument.java
@@ -18,67 +18,81 @@ package android.print.pdf;
import android.content.Context;
import android.graphics.Rect;
+import android.graphics.pdf.PdfDocument;
+import android.graphics.pdf.PdfDocument.Page;
+import android.graphics.pdf.PdfDocument.PageInfo;
import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
-import android.print.pdf.PdfDocument;
-import android.print.pdf.PdfDocument.Page;
-import android.print.pdf.PdfDocument.PageInfo;
-
-import java.io.OutputStream;
-import java.util.List;
/**
- * This class is a helper for printing content to a different media
- * size. This class is responsible for computing a correct page size
- * given some print constraints, i.e. {@link PrintAttributes}. It is
- * an adapter around a {@link PdfDocument}.
+ * This class is a helper for creating a PDF file for given print
+ * attributes. It is useful for implementing printing via the native
+ * Android graphics APIs.
+ *
+ * // open a new document
+ * PrintedPdfDocument document = new PrintedPdfDocument(context,
+ * printAttributes);
+ *
+ * // start a page
+ * Page page = document.startPage(0);
+ *
+ * // draw something on the page
+ * View content = getContentView();
+ * content.draw(page.getCanvas());
+ *
+ * // finish the page
+ * document.finishPage(page);
+ * . . .
+ * // add more pages
+ * . . .
+ * // write the document content
+ * document.writeTo(getOutputStream());
+ *
+ * //close the document
+ * document.close();
+ *
*/
-public final class PrintedPdfDocument {
+public class PrintedPdfDocument extends PdfDocument {
private static final int MILS_PER_INCH = 1000;
private static final int POINTS_IN_INCH = 72;
- private final PdfDocument mDocument = PdfDocument.open();
- private final Rect mPageSize = new Rect();
- private final Rect mContentSize = new Rect();
+ private final int mPageWidth;
+ private final int mPageHeight;
+ private final Rect mContentRect;
/**
- * Opens a new document. The document pages are computed based on
- * the passes in {@link PrintAttributes}.
+ * Creates a new document.
*
A print service implementation should extend {@link android.printservice.PrintService} and implement its abstract methods. Also the print service has to follow the contract for -managing print {@link android.printservice.PrintJob}s. +managing {@link android.printservice.PrintJob}s.
The system is responsible for starting and stopping a print service depending on whether
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d5d746a2a6016..f78d8075149dc 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -118,6 +118,7 @@ LOCAL_SRC_FILES:= \
android/graphics/Utils.cpp \
android/graphics/Xfermode.cpp \
android/graphics/YuvToJpegEncoder.cpp \
+ android/graphics/pdf/PdfDocument.cpp \
android_media_AudioRecord.cpp \
android_media_AudioSystem.cpp \
android_media_AudioTrack.cpp \
@@ -135,7 +136,6 @@ LOCAL_SRC_FILES:= \
android_util_FileObserver.cpp \
android/opengl/poly_clip.cpp.arm \
android/opengl/util.cpp.arm \
- android/print/android_print_pdf_PdfDocument.cpp \
android_server_NetworkManagementSocketTagger.cpp \
android_server_Watchdog.cpp \
android_ddm_DdmHandleNativeHeap.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 490d85c4b707f..8518101432803 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -117,6 +117,7 @@ extern int register_android_graphics_Rasterizer(JNIEnv* env);
extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
extern int register_android_graphics_Xfermode(JNIEnv* env);
+extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
extern int register_android_view_GraphicBuffer(JNIEnv* env);
extern int register_android_view_GLES20DisplayList(JNIEnv* env);
@@ -144,7 +145,6 @@ extern int register_android_os_FileObserver(JNIEnv *env);
extern int register_android_os_FileUtils(JNIEnv *env);
extern int register_android_os_UEventObserver(JNIEnv* env);
extern int register_android_os_MemoryFile(JNIEnv* env);
-extern int register_android_print_pdf_PdfDocument(JNIEnv* env);
extern int register_android_net_LocalSocketImpl(JNIEnv* env);
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_net_TrafficStats(JNIEnv* env);
@@ -1161,6 +1161,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_Xfermode),
REG_JNI(register_android_graphics_YuvImage),
+ REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_database_CursorWindow),
REG_JNI(register_android_database_SQLiteConnection),
@@ -1173,7 +1174,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_SELinux),
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
- REG_JNI(register_android_print_pdf_PdfDocument),
REG_JNI(register_android_net_LocalSocketImpl),
REG_JNI(register_android_net_NetworkUtils),
REG_JNI(register_android_net_TrafficStats),
diff --git a/core/jni/android/print/android_print_pdf_PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
similarity index 74%
rename from core/jni/android/print/android_print_pdf_PdfDocument.cpp
rename to core/jni/android/graphics/pdf/PdfDocument.cpp
index 3daad5c5acc6c..b57a0fe8aa10a 100644
--- a/core/jni/android/print/android_print_pdf_PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -28,6 +28,8 @@
namespace android {
+#define LOGD(x...) do { Log::Instance()->printf(Log::ELogD, x); } while(0)
+
static jint nativeCreateDocument(JNIEnv* env, jobject clazz) {
return reinterpret_cast
* A typical use of the APIs looks like this: *
*- * // open a new document - * PdfDocument document = PdfDocument.open(); + * // create a new document + * PdfDocument document = new PdfDocument(); * * // crate a page description * PageInfo pageInfo = new PageInfo.Builder(new Rect(0, 0, 100, 100), 1).create(); @@ -65,7 +67,7 @@ import java.util.List; * document.close(); **/ -public final class PdfDocument { +public class PdfDocument { private final byte[] mChunk = new byte[4096]; @@ -77,25 +79,10 @@ public final class PdfDocument { private Page mCurrentPage; - /** - * Opens a new document. - *
- * Note: You must close the document after you are - * done by calling {@link #close()} - *
- * - * @return The document. - * - * @see #close() - */ - public static PdfDocument open() { - return new PdfDocument(); - } - /** * Creates a new instance. */ - private PdfDocument() { + public PdfDocument() { mNativeDocument = nativeCreateDocument(); mCloseGuard.open("close"); } @@ -109,23 +96,24 @@ public final class PdfDocument { * no longer access the page or its canvas. ** Note: Do not call this method after {@link #close()}. + * Also do not call this method if the last page returned by this method + * is not finished by calling {@link #finishPage(Page)}. *
* - * @param pageInfo The page info. + * @param pageInfo The page info. Cannot be null. * @return A blank page. * * @see #finishPage(Page) */ public Page startPage(PageInfo pageInfo) { throwIfClosed(); + throwIfCurrentPageNotFinished(); if (pageInfo == null) { - throw new IllegalArgumentException("page cannot be null!"); + throw new IllegalArgumentException("page cannot be null"); } - if (mCurrentPage != null) { - throw new IllegalStateException("Previous page not finished!"); - } - Canvas canvas = new PdfCanvas(nativeCreatePage(pageInfo.mPageSize, - pageInfo.mContentSize, pageInfo.mInitialTransform.native_instance)); + Canvas canvas = new PdfCanvas(nativeCreatePage(pageInfo.mPageWidth, + pageInfo.mPageHeight, pageInfo.mContentRect.left, pageInfo.mContentRect.top, + pageInfo.mContentRect.right, pageInfo.mContentRect.bottom)); mCurrentPage = new Page(canvas, pageInfo); return mCurrentPage; } @@ -134,9 +122,10 @@ public final class PdfDocument { * Finishes a started page. You should always finish the last started page. ** Note: Do not call this method after {@link #close()}. + * You should not finish the same page more than once. *
* - * @param page The page. + * @param page The page. Cannot be null. * * @see #startPage(PageInfo) */ @@ -148,6 +137,9 @@ public final class PdfDocument { if (page != mCurrentPage) { throw new IllegalStateException("invalid page"); } + if (page.isFinished()) { + throw new IllegalStateException("page already finished"); + } mPages.add(page.getInfo()); mCurrentPage = null; nativeAppendPage(mNativeDocument, page.mCanvas.mNativeCanvas); @@ -155,15 +147,21 @@ public final class PdfDocument { } /** - * Writes the document to an output stream. + * Writes the document to an output stream. You can call this method + * multiple times. ** Note: Do not call this method after {@link #close()}. + * Also do not call this method if a page returned by {@link #startPage( + * PageInfo)} is not finished by calling {@link #finishPage(Page)}. *
* - * @param out The output stream. + * @param out The output stream. Cannot be null. + * + * @throws IOException If an error occurs while writing. */ - public void writeTo(OutputStream out) { + public void writeTo(OutputStream out) throws IOException { throwIfClosed(); + throwIfCurrentPageNotFinished(); if (out == null) { throw new IllegalArgumentException("out cannot be null!"); } @@ -173,7 +171,7 @@ public final class PdfDocument { /** * Gets the pages of the document. * - * @return The pages. + * @return The pages or an empty list. */ public List+ * Note: Do not call this method if the page + * returned by {@link #startPage(PageInfo)} is not finished by + * calling {@link #finishPage(Page)}. + *
*/ public void close() { + throwIfCurrentPageNotFinished(); dispose(); } @@ -215,6 +219,15 @@ public final class PdfDocument { } } + /** + * Throws an exception if the last started page is not finished. + */ + private void throwIfCurrentPageNotFinished() { + if (mCurrentPage != null) { + throw new IllegalStateException("Current page not finished!"); + } + } + private native int nativeCreateDocument(); private native void nativeFinalize(int document); @@ -223,9 +236,8 @@ public final class PdfDocument { private native void nativeWriteTo(int document, OutputStream out, byte[] chunk); - private static native int nativeCreatePage(Rect pageSize, - Rect contentSize, int nativeMatrix); - + private static native int nativeCreatePage(int pageWidth, int pageHeight, int contentLeft, + int contentTop, int contentRight, int contentBottom); private final class PdfCanvas extends Canvas { @@ -243,9 +255,9 @@ public final class PdfDocument { * This class represents meta-data that describes a PDF {@link Page}. */ public static final class PageInfo { - private Rect mPageSize; - private Rect mContentSize; - private Matrix mInitialTransform; + private int mPageWidth; + private int mPageHeight; + private Rect mContentRect; private int mPageNumber; /** @@ -256,32 +268,32 @@ public final class PdfDocument { } /** - * Gets the page size in PostScript points (1/72th of an inch). + * Gets the page width in PostScript points (1/72th of an inch). * - * @return The page size. + * @return The page width. */ - public Rect getPageSize() { - return mPageSize; + public int getPageWidth() { + return mPageWidth; } /** - * Get the content size in PostScript points (1/72th of an inch). + * Gets the page height in PostScript points (1/72th of an inch). * - * @return The content size. + * @return The page height. */ - public Rect getContentSize() { - return mContentSize; + public int getPageHeight() { + return mPageHeight; } /** - * Gets the initial transform which is applied to the page. This may be - * useful to move the origin to account for a margin, apply scale, or - * apply a rotation. + * Get the content rectangle in PostScript points (1/72th of an inch). + * This is the area that contains the page content and is relative to + * the page top left. * - * @return The initial transform. + * @return The content rectangle. */ - public Matrix getInitialTransform() { - return mInitialTransform; + public Rect getContentRect() { + return mContentRect; } /** @@ -302,47 +314,40 @@ public final class PdfDocument { /** * Creates a new builder with the mandatory page info attributes. * - * @param pageSize The page size in PostScript (1/72th of an inch). + * @param pageWidth The page width in PostScript (1/72th of an inch). + * @param pageHeight The page height in PostScript (1/72th of an inch). * @param pageNumber The page number. */ - public Builder(Rect pageSize, int pageNumber) { - if (pageSize.width() == 0 || pageSize.height() == 0) { - throw new IllegalArgumentException("page width and height" + - " must be greater than zero!"); + public Builder(int pageWidth, int pageHeight, int pageNumber) { + if (pageWidth <= 0) { + throw new IllegalArgumentException("page width must be positive"); + } + if (pageHeight <= 0) { + throw new IllegalArgumentException("page width must be positive"); } if (pageNumber < 0) { - throw new IllegalArgumentException("pageNumber cannot be less than zero!"); + throw new IllegalArgumentException("pageNumber must be non negative"); } - mPageInfo.mPageSize = pageSize; + mPageInfo.mPageWidth = pageWidth; + mPageInfo.mPageHeight = pageHeight; mPageInfo.mPageNumber = pageNumber; } /** - * Sets the content size in PostScript point (1/72th of an inch). + * Sets the content rectangle in PostScript point (1/72th of an inch). + * This is the area that contains the page content and is relative to + * the page top left. * - * @param contentSize The content size. + * @param contentRect The content rectangle. Must fit in the page. */ - public Builder setContentSize(Rect contentSize) { - Rect pageSize = mPageInfo.mPageSize; - if (contentSize != null && (pageSize.left > contentSize.left - || pageSize.top > contentSize.top - || pageSize.right < contentSize.right - || pageSize.bottom < contentSize.bottom)) { - throw new IllegalArgumentException("contentSize does not fit the pageSize!"); + public Builder setContentRect(Rect contentRect) { + if (contentRect != null && (contentRect.left < 0 + || contentRect.top < 0 + || contentRect.right > mPageInfo.mPageWidth + || contentRect.bottom > mPageInfo.mPageHeight)) { + throw new IllegalArgumentException("contentRect does not fit the page"); } - mPageInfo.mContentSize = contentSize; - return this; - } - - /** - * Sets the initial transform which is applied to the page. This may be - * useful to move the origin to account for a margin, apply scale, or - * apply a rotation. - * - * @param transform The initial transform. - */ - public Builder setInitialTransform(Matrix transform) { - mPageInfo.mInitialTransform = transform; + mPageInfo.mContentRect = contentRect; return this; } @@ -352,11 +357,9 @@ public final class PdfDocument { * @return The new instance. */ public PageInfo create() { - if (mPageInfo.mContentSize == null) { - mPageInfo.mContentSize = mPageInfo.mPageSize; - } - if (mPageInfo.mInitialTransform == null) { - mPageInfo.mInitialTransform = new Matrix(); + if (mPageInfo.mContentRect == null) { + mPageInfo.mContentRect = new Rect(0, 0, + mPageInfo.mPageWidth, mPageInfo.mPageHeight); } return mPageInfo; } @@ -367,7 +370,8 @@ public final class PdfDocument { * This class represents a PDF document page. It has associated * a canvas on which you can draw content and is acquired by a * call to {@link #getCanvas()}. It also has associated a - * {@link PageInfo} instance that describes its attributes. + * {@link PageInfo} instance that describes its attributes. Also + * a page has */ public static final class Page { private final PageInfo mPageInfo; @@ -406,6 +410,10 @@ public final class PdfDocument { return mPageInfo; } + boolean isFinished() { + return mCanvas == null; + } + private void finish() { if (mCanvas != null) { mCanvas.release(); diff --git a/graphics/java/android/graphics/pdf/package.html b/graphics/java/android/graphics/pdf/package.html new file mode 100644 index 0000000000000..51f246011df34 --- /dev/null +++ b/graphics/java/android/graphics/pdf/package.html @@ -0,0 +1,5 @@ + + +Contains classes for manipulation of PDF content. + + \ No newline at end of file diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index 34e87cc2e3d7d..af1c60e742a22 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -358,7 +358,7 @@ public class PrintJobConfigActivity extends Activity { PrintSpoolerService.peekInstance().setPrintJobAttributesNoPersistence( mPrintJobId, mCurrPrintAttributes); - mMetadata.putBoolean(PrintDocumentAdapter.METADATA_KEY_PRINT_PREVIEW, + mMetadata.putBoolean(PrintDocumentAdapter.EXTRA_PRINT_PREVIEW, !mEditor.isPrintConfirmed()); mControllerState = CONTROLLER_STATE_LAYOUT_STARTED;