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@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@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