| Joe Fernandez | 0f73793 | 2013-10-29 01:55:12 -0700 | [diff] [blame] | 1 | page.title=Printing Custom Documents |
| 2 | parent.title=Printing Content |
| 3 | parent.link=index.html |
| 4 | |
| 5 | trainingnavtop=true |
| 6 | next.title= |
| 7 | next.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> |
| 53 | private 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 | @Override |
| 141 | public 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> |
| 195 | private 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 | @Override |
| 247 | public 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> |
| 332 | private 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> |