1page.title=Printing Custom Documents
2parent.title=Printing Content
3parent.link=index.html
4
5trainingnavtop=true
6next.title=
7next.link=
8
9@jd:body
10
11<div id="tb-wrapper">
12<div id="tb">
13
14<h2>This lesson teaches you to</h2>
15<ol>
16  <li><a href="#print-manager">Connect to the Print Manager</a></li>
17  <li><a href="#print-adapter">Create a Print Adapter</a>
18    <ol>
19      <li><a href="#doc-info">Compute print document info</a></li>
20      <li><a href="#write-file">Write a print document file</a></li>
21    </ol>
22  </li>
23  <li><a href="#draw-content">Drawing PDF Page Content</a></li>
24</ol>
25
26</div>
27</div>
28
29<p>For some applications, such as drawing apps, page layout apps and other apps that focus on
30  graphic output, creating beautiful printed pages is a key feature. In this case, it is not enough
31  to print an image or an HTML document. The print output for these types of applications requires
32  precise control of everything that goes into a page, including fonts, text flow, page breaks,
33  headers, footers, and graphic elements.</p>
34
35<p>Creating print output that is completely customized for your application requires more
36  programming investment than the previously discussed approaches. You must build components that
37  communicate with the print framework, adjust to printer settings, draw page elements and
38  manage printing on multiple pages.</p>
39
40<p>This lesson shows you how you connect with the print manager, create a print adapter and
41  build content for printing.</p>
42
43
44<h2 id="print-manager">Connect to the Print Manager</h2>
45
46<p>When your application manages the printing process directly, the first step after receiving a
47  print request from your user is to connect to the Android print framework and obtain an instance
48  of the {@link android.print.PrintManager} class. This class allows you to initialize a print job
49  and begin the printing lifecycle. The following code example shows how to get the print manager
50  and start the printing process.</p>
51
52<pre>
53private void doPrint() {
54    // Get a PrintManager instance
55    PrintManager printManager = (PrintManager) getActivity()
56            .getSystemService(Context.PRINT_SERVICE);
57
58    // Set job name, which will be displayed in the print queue
59    String jobName = getActivity().getString(R.string.app_name) + " Document";
60
61    // Start a print job, passing in a PrintDocumentAdapter implementation
62    // to handle the generation of a print document
63    printManager.print(jobName, new MyPrintDocumentAdapter(getActivity()),
64            null); //
65}
66</pre>
67
68<p>The example code above demonstrates how to name a print job and set an instance of the {@link
69  android.print.PrintDocumentAdapter} class which handles the steps of the printing lifecycle. The
70  implementation of the print adapter class is discussed in the next section.</p>
71
72<p class="note">
73  <strong>Note:</strong> The last parameter in the {@link android.print.PrintManager#print print()}
74  method takes a {@link android.print.PrintAttributes} object. You can use this parameter to
75  provide hints to the printing framework and pre-set options based on the previous printing cycle,
76  thereby improving the user experience. You may also use this parameter to set options that are
77  more appropriate to the content being printed, such as setting the orientation to landscape
78  when printing a photo that is in that orientation.
79</p>
80
81
82<h2 id="print-adapter">Create a Print Adapter</h2>
83
84<p>A print adapter interacts with the Android print framework and handles the steps of the
85  printing process. This process requires users to select printers and print options before creating
86  a document for printing. These selections can influence the final output as the user chooses
87  printers with different output capabilities, different page sizes, or different page orientations.
88  As these selections are made, the print framework asks your adapter to lay out and generate a
89  print document, in preparation for final output. Once a user taps the print button, the framework
90  takes the final print document and passes it to a print provider for output. During the printing
91  process, users can choose to cancel the print action, so your print adapter must also listen for
92  and react to a cancellation requests.</p>
93
94<p>The {@link android.print.PrintDocumentAdapter} abstract class is designed to handle the
95  printing lifecycle, which has four main callback methods. You must implement these methods
96  in your print adapter in order to interact properly with the print framework:</p>
97
98<ul>
99  <li>{@link android.print.PrintDocumentAdapter#onStart onStart()} - Called once at the
100    beginning of the print process. If your application has any one-time preparation tasks to
101    perform, such as getting a snapshot of the data to be printed, execute them here. Implementing
102    this method in your adapter is not required.</li>
103  <li>{@link android.print.PrintDocumentAdapter#onLayout onLayout()} - Called each time a
104    user changes a print setting which impacts the output, such as a different page size,
105    or page orientation, giving your application an opportunity to compute the layout of the
106    pages to be printed. At the minimum, this method must return how many pages are expected
107    in the printed document.</li>
108  <li>{@link android.print.PrintDocumentAdapter#onWrite onWrite()} - Called to render printed
109    pages into a file to be printed. This method may be called one or more times after each
110    {@link android.print.PrintDocumentAdapter#onLayout onLayout()} call.</li>
111  <li>{@link android.print.PrintDocumentAdapter#onFinish onFinish()} - Called once at the end
112    of the print process. If your application has any one-time tear-down tasks to perform,
113    execute them here. Implementing this method in your adapter is not required.</li>
114</ul>
115
116<p>The following sections describe how to implement the layout and write methods, which are
117  critical to the functioning of a print adapter.</p>
118
119<p class="note">
120  <strong>Note:</strong> These adapter methods are called on the main thread of your application. If
121  you expect the execution of these methods in your implementation to take a significant amount of
122  time, implement them to execute within a separate thread. For example, you can encapsulate the
123  layout or print document writing work in separate {@link android.os.AsyncTask} objects.
124</p>
125
126
127<h3 id="doc-info">Compute print document info</h3>
128
129<p>Within an implementation of the {@link android.print.PrintDocumentAdapter} class, your
130  application must be able to specify the type of document it is creating and calculate the total
131  number of pages for print job, given information about the printed page size.
132  The implementation of the {@link android.print.PrintDocumentAdapter#onLayout onLayout()} method in
133  the adapter makes these calculations and provides information about the expected output of the
134  print job in a {@link android.print.PrintDocumentInfo} class, including the number of pages and
135  content type. The following code example shows a basic implementation of the {@link
136  android.print.PrintDocumentAdapter#onLayout onLayout()} method for a {@link
137  android.print.PrintDocumentAdapter}:
138
139<pre>
140&#64;Override
141public void onLayout(PrintAttributes oldAttributes,
142                     PrintAttributes newAttributes,
143                     CancellationSignal cancellationSignal,
144                     LayoutResultCallback callback,
145                     Bundle metadata) {
146    // Create a new PdfDocument with the requested page attributes
147    mPdfDocument = new PrintedPdfDocument(getActivity(), newAttributes);
148
149    // Respond to cancellation request
150    if (cancellationSignal.isCancelled() ) {
151        callback.onLayoutCancelled();
152        return;
153    }
154
155    // Compute the expected number of printed pages
156    int pages = computePageCount(newAttributes);
157
158    if (pages > 0) {
159        // Return print information to print framework
160        PrintDocumentInfo info = new PrintDocumentInfo
161                .Builder("print_output.pdf")
162                .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
163                .setPageCount(pages);
164                .build();
165        // Content layout reflow is complete
166        callback.onLayoutFinished(info, true);
167    } else {
168        // Otherwise report an error to the print framework
169        callback.onLayoutFailed("Page count calculation failed.");
170    }
171}
172</pre>
173
174<p>The execution of {@link android.print.PrintDocumentAdapter#onLayout onLayout()} method can
175  have three outcomes: completion, cancellation, or failure in the case where calculation of the
176  layout cannot be completed. You must indicate one of these results by calling the appropriate
177  method of the {@link android.print.PrintDocumentAdapter.LayoutResultCallback} object.</p>
178
179<p class="note">
180  <strong>Note:</strong> The boolean parameter of the
181  {@link android.print.PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished
182  onLayoutFinished()} method indicates whether or not the layout content has actually changed
183  since the last request. Setting this parameter properly allows the print framework to avoid
184  unnecessarily calling the {@link android.print.PrintDocumentAdapter#onWrite onWrite()} method,
185  essentially caching the previously written print document and improving performance.
186</p>
187
188<p>The main work of {@link android.print.PrintDocumentAdapter#onLayout onLayout()} is
189  calculating the number of pages that are expected as output given the attributes of the printer.
190  How you calculate this number is highly dependent on how your application lays out pages for
191  printing. The following code example shows an implementation where the number of pages is
192  determined by the print orientation:</p>
193
194<pre>
195private int computePageCount(PrintAttributes printAttributes) {
196    int itemsPerPage = 4; // default item count for portrait mode
197
198    MediaSize pageSize = printAttributes.getMediaSize();
199    if (!pageSize.isPortrait()) {
200        // Six items per page in landscape orientation
201        itemsPerPage = 6;
202    }
203
204    // Determine number of print items
205    int printItemCount = getPrintItemCount();
206
207    return (int) Math.ceil(printItemCount / itemsPerPage);
208}
209</pre>
210
211
212<h3 id="write-file">Write a print document file</h3>
213
214<p>When it is time to write print output to a file, the Android print framework calls the {@link
215  android.print.PrintDocumentAdapter#onWrite onWrite()} method of your application's {@link
216  android.print.PrintDocumentAdapter} class. The method's parameters specify which pages should be
217  written and the output file to be used. Your implementation of this method must then render each
218  requested page of content to a multi-page PDF document file. When this process is complete, you
219  call the {@link android.print.PrintDocumentAdapter.WriteResultCallback#onWriteFinished
220  onWriteFinished()} method of the callback object.</p>
221
222<p class="note">
223  <strong>Note:</strong> The Android print framework may call the {@link
224  android.print.PrintDocumentAdapter#onWrite onWrite()} method one or more times for every
225  call to {@link android.print.PrintDocumentAdapter#onLayout onLayout()}. For this reason, it is
226  important to set the boolean parameter of
227  {@link android.print.PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished
228  onLayoutFinished()} method to {@code false} when the print content layout has not changed,
229  to avoid unnecessary re-writes of the print document.
230</p>
231
232<p class="note">
233  <strong>Note:</strong> The boolean parameter of the
234  {@link android.print.PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished
235  onLayoutFinished()} method indicates whether or not the layout content has actually changed
236  since the last request. Setting this parameter properly allows the print framework to avoid
237  unnecessarily calling the {@link android.print.PrintDocumentAdapter#onLayout onLayout()} method,
238  essentially caching the previously written print document and improving performance.
239</p>
240
241
242<p>The following sample demonstrates the basic mechanics of this process using the {@link
243  android.print.pdf.PrintedPdfDocument} class to create a PDF file:</p>
244
245<pre>
246&#64;Override
247public void onWrite(final PageRange[] pageRanges,
248                    final ParcelFileDescriptor destination,
249                    final CancellationSignal cancellationSignal,
250                    final WriteResultCallback callback) {
251    // Iterate over each page of the document,
252    // check if it's in the output range.
253    for (int i = 0; i < totalPages; i++) {
254        // Check to see if this page is in the output range.
255        if (containsPage(pageRanges, i)) {
256            // If so, add it to writtenPagesArray. writtenPagesArray.size()
257            // is used to compute the next output page index.
258            writtenPagesArray.append(writtenPagesArray.size(), i);
259            PdfDocument.Page page = mPdfDocument.startPage(i);
260
261            // check for cancellation
262            if (cancellationSignal.isCancelled()) {
263                callback.onWriteCancelled();
264                mPdfDocument.close();
265                mPdfDocument = null;
266                return;
267            }
268
269            // Draw page content for printing
270            drawPage(page);
271
272            // Rendering is complete, so page can be finalized.
273            mPdfDocument.finishPage(page);
274        }
275    }
276
277    // Write PDF document to file
278    try {
279        mPdfDocument.writeTo(new FileOutputStream(
280                destination.getFileDescriptor()));
281    } catch (IOException e) {
282        callback.onWriteFailed(e.toString());
283        return;
284    } finally {
285        mPdfDocument.close();
286        mPdfDocument = null;
287    }
288    PageRange[] writtenPages = computeWrittenPages();
289    // Signal the print framework the document is complete
290    callback.onWriteFinished(writtenPages);
291
292    ...
293}
294</pre>
295
296<p>This sample delegates rendering of PDF page content to <code>drawPage()</code>
297  method, which is discussed in the next section.
298</p>
299
300<p>As with layout, execution of {@link android.print.PrintDocumentAdapter#onWrite onWrite()}
301  method can have three outcomes: completion, cancellation, or failure in the case where the
302  the content cannot be written. You must indicate one of these results by calling the
303  appropriate method of the {@link android.print.PrintDocumentAdapter.WriteResultCallback} object.
304  </p>
305
306
307<p class="note">
308  <strong>Note:</strong> Rendering a document for printing can be a resource-intensive operation. In
309  order to avoid blocking the main user interface thread of your application, you should consider
310  performing the page rendering and writing operations on a separate thread, for example
311  in an {@link android.os.AsyncTask}.
312  For more information about working with execution threads like asynchronous tasks,
313  see <a href="{@docRoot}guide/components/processes-and-threads.html">Processes
314    and Threads</a>.
315</p>
316
317
318<h2 id="draw-content">Drawing PDF Page Content</h2>
319
320<p>When your application prints, your application must generate a PDF document and pass it to
321  the Android print framework for printing. You can use any PDF generation library for this
322  purpose. This lesson shows how to use the {@link android.print.pdf.PrintedPdfDocument} class
323  to generate PDF pages from your content.</p>
324
325<p>The {@link android.print.pdf.PrintedPdfDocument} class uses a {@link android.graphics.Canvas}
326  object to draw elements on an PDF page, similar to drawing on an activity layout. You can draw
327  elements on the printed page using the {@link android.graphics.Canvas} draw methods. The following
328  example code demonstrates how to draw some simple elements on a PDF document page using these
329  methods:</p>
330
331<pre>
332private void drawPage(PdfDocument.Page page) {
333    Canvas canvas = page.getCanvas();
334
335    // units are in points (1/72 of an inch)
336    int titleBaseLine = 72;
337    int leftMargin = 54;
338
339    Paint paint = new Paint();
340    paint.setColor(Color.BLACK);
341    paint.setTextSize(36);
342    canvas.drawText("Test Title", leftMargin, titleBaseLine, paint);
343
344    paint.setTextSize(11);
345    canvas.drawText("Test paragraph", leftMargin, titleBaseLine + 25, paint);
346
347    paint.setColor(Color.BLUE);
348    canvas.drawRect(100, 100, 172, 172, paint);
349}
350</pre>
351
352<p>When using {@link android.graphics.Canvas} to draw on a PDF page, elements are specified in
353  points, which is 1/72 of an inch. Make sure you use this unit of measure for specifying the size
354  of elements on the page. For positioning of drawn elements, the coordinate system starts at 0,0
355  for the top left corner of the page.</p>
356
357<p>
358  <strong>Tip:</strong> While the {@link android.graphics.Canvas} object allows you to place print
359  elements on the edge of a PDF document, many printers are not able to print to the edge of a
360  physical piece of paper. Make sure that you account for the unprintable edges of the page when
361  you build a print document with this class.
362</p>
363