Home | History | Annotate | Download | only in printing
      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 &#64;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 &#64;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>
    363