363 lines
16 KiB
Plaintext
363 lines
16 KiB
Plaintext
page.title=Printing Custom Documents
|
|
parent.title=Printing Content
|
|
parent.link=index.html
|
|
|
|
trainingnavtop=true
|
|
next.title=
|
|
next.link=
|
|
|
|
@jd:body
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
|
|
<h2>This lesson teaches you to</h2>
|
|
<ol>
|
|
<li><a href="#print-manager">Connect to the Print Manager</a></li>
|
|
<li><a href="#print-adapter">Create a Print Adapter</a>
|
|
<ol>
|
|
<li><a href="#doc-info">Compute print document info</a></li>
|
|
<li><a href="#write-file">Write a print document file</a></li>
|
|
</ol>
|
|
</li>
|
|
<li><a href="#draw-content">Drawing PDF Page Content</a></li>
|
|
</ol>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<p>For some applications, such as drawing apps, page layout apps and other apps that focus on
|
|
graphic output, creating beautiful printed pages is a key feature. In this case, it is not enough
|
|
to print an image or an HTML document. The print output for these types of applications requires
|
|
precise control of everything that goes into a page, including fonts, text flow, page breaks,
|
|
headers, footers, and graphic elements.</p>
|
|
|
|
<p>Creating print output that is completely customized for your application requires more
|
|
programming investment than the previously discussed approaches. You must build components that
|
|
communicate with the print framework, adjust to printer settings, draw page elements and
|
|
manage printing on multiple pages.</p>
|
|
|
|
<p>This lesson shows you how you connect with the print manager, create a print adapter and
|
|
build content for printing.</p>
|
|
|
|
|
|
<h2 id="print-manager">Connect to the Print Manager</h2>
|
|
|
|
<p>When your application manages the printing process directly, the first step after receiving a
|
|
print request from your user is to connect to the Android print framework and obtain an instance
|
|
of the {@link android.print.PrintManager} class. This class allows you to initialize a print job
|
|
and begin the printing lifecycle. The following code example shows how to get the print manager
|
|
and start the printing process.</p>
|
|
|
|
<pre>
|
|
private void doPrint() {
|
|
// Get a PrintManager instance
|
|
PrintManager printManager = (PrintManager) getActivity()
|
|
.getSystemService(Context.PRINT_SERVICE);
|
|
|
|
// Set job name, which will be displayed in the print queue
|
|
String jobName = getActivity().getString(R.string.app_name) + " Document";
|
|
|
|
// Start a print job, passing in a PrintDocumentAdapter implementation
|
|
// to handle the generation of a print document
|
|
printManager.print(jobName, new MyPrintDocumentAdapter(getActivity()),
|
|
null); //
|
|
}
|
|
</pre>
|
|
|
|
<p>The example code above demonstrates how to name a print job and set an instance of the {@link
|
|
android.print.PrintDocumentAdapter} class which handles the steps of the printing lifecycle. The
|
|
implementation of the print adapter class is discussed in the next section.</p>
|
|
|
|
<p class="note">
|
|
<strong>Note:</strong> The last parameter in the {@link android.print.PrintManager#print print()}
|
|
method takes a {@link android.print.PrintAttributes} object. You can use this parameter to
|
|
provide hints to the printing framework and pre-set options based on the previous printing cycle,
|
|
thereby improving the user experience. You may also use this parameter to set options that are
|
|
more appropriate to the content being printed, such as setting the orientation to landscape
|
|
when printing a photo that is in that orientation.
|
|
</p>
|
|
|
|
|
|
<h2 id="print-adapter">Create a Print Adapter</h2>
|
|
|
|
<p>A print adapter interacts with the Android print framework and handles the steps of the
|
|
printing process. This process requires users to select printers and print options before creating
|
|
a document for printing. These selections can influence the final output as the user chooses
|
|
printers with different output capabilities, different page sizes, or different page orientations.
|
|
As these selections are made, the print framework asks your adapter to lay out and generate a
|
|
print document, in preparation for final output. Once a user taps the print button, the framework
|
|
takes the final print document and passes it to a print provider for output. During the printing
|
|
process, users can choose to cancel the print action, so your print adapter must also listen for
|
|
and react to a cancellation requests.</p>
|
|
|
|
<p>The {@link android.print.PrintDocumentAdapter} abstract class is designed to handle the
|
|
printing lifecycle, which has four main callback methods. You must implement these methods
|
|
in your print adapter in order to interact properly with the print framework:</p>
|
|
|
|
<ul>
|
|
<li>{@link android.print.PrintDocumentAdapter#onStart onStart()} - Called once at the
|
|
beginning of the print process. If your application has any one-time preparation tasks to
|
|
perform, such as getting a snapshot of the data to be printed, execute them here. Implementing
|
|
this method in your adapter is not required.</li>
|
|
<li>{@link android.print.PrintDocumentAdapter#onLayout onLayout()} - Called each time a
|
|
user changes a print setting which impacts the output, such as a different page size,
|
|
or page orientation, giving your application an opportunity to compute the layout of the
|
|
pages to be printed. At the minimum, this method must return how many pages are expected
|
|
in the printed document.</li>
|
|
<li>{@link android.print.PrintDocumentAdapter#onWrite onWrite()} - Called to render printed
|
|
pages into a file to be printed. This method may be called one or more times after each
|
|
{@link android.print.PrintDocumentAdapter#onLayout onLayout()} call.</li>
|
|
<li>{@link android.print.PrintDocumentAdapter#onFinish onFinish()} - Called once at the end
|
|
of the print process. If your application has any one-time tear-down tasks to perform,
|
|
execute them here. Implementing this method in your adapter is not required.</li>
|
|
</ul>
|
|
|
|
<p>The following sections describe how to implement the layout and write methods, which are
|
|
critical to the functioning of a print adapter.</p>
|
|
|
|
<p class="note">
|
|
<strong>Note:</strong> These adapter methods are called on the main thread of your application. If
|
|
you expect the execution of these methods in your implementation to take a significant amount of
|
|
time, implement them to execute within a separate thread. For example, you can encapsulate the
|
|
layout or print document writing work in separate {@link android.os.AsyncTask} objects.
|
|
</p>
|
|
|
|
|
|
<h3 id="doc-info">Compute print document info</h3>
|
|
|
|
<p>Within an implementation of the {@link android.print.PrintDocumentAdapter} class, your
|
|
application must be able to specify the type of document it is creating and calculate the total
|
|
number of pages for print job, given information about the printed page size.
|
|
The implementation of the {@link android.print.PrintDocumentAdapter#onLayout onLayout()} method in
|
|
the adapter makes these calculations and provides information about the expected output of the
|
|
print job in a {@link android.print.PrintDocumentInfo} class, including the number of pages and
|
|
content type. The following code example shows a basic implementation of the {@link
|
|
android.print.PrintDocumentAdapter#onLayout onLayout()} method for a {@link
|
|
android.print.PrintDocumentAdapter}:
|
|
|
|
<pre>
|
|
@Override
|
|
public void onLayout(PrintAttributes oldAttributes,
|
|
PrintAttributes newAttributes,
|
|
CancellationSignal cancellationSignal,
|
|
LayoutResultCallback callback,
|
|
Bundle metadata) {
|
|
// Create a new PdfDocument with the requested page attributes
|
|
mPdfDocument = new PrintedPdfDocument(getActivity(), newAttributes);
|
|
|
|
// Respond to cancellation request
|
|
if (cancellationSignal.isCancelled() ) {
|
|
callback.onLayoutCancelled();
|
|
return;
|
|
}
|
|
|
|
// Compute the expected number of printed pages
|
|
int pages = computePageCount(newAttributes);
|
|
|
|
if (pages > 0) {
|
|
// Return print information to print framework
|
|
PrintDocumentInfo info = new PrintDocumentInfo
|
|
.Builder("print_output.pdf")
|
|
.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
|
|
.setPageCount(pages);
|
|
.build();
|
|
// Content layout reflow is complete
|
|
callback.onLayoutFinished(info, true);
|
|
} else {
|
|
// Otherwise report an error to the print framework
|
|
callback.onLayoutFailed("Page count calculation failed.");
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>The execution of {@link android.print.PrintDocumentAdapter#onLayout onLayout()} method can
|
|
have three outcomes: completion, cancellation, or failure in the case where calculation of the
|
|
layout cannot be completed. You must indicate one of these results by calling the appropriate
|
|
method of the {@link android.print.PrintDocumentAdapter.LayoutResultCallback} object.</p>
|
|
|
|
<p class="note">
|
|
<strong>Note:</strong> The boolean parameter of the
|
|
{@link android.print.PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished
|
|
onLayoutFinished()} method indicates whether or not the layout content has actually changed
|
|
since the last request. Setting this parameter properly allows the print framework to avoid
|
|
unnecessarily calling the {@link android.print.PrintDocumentAdapter#onWrite onWrite()} method,
|
|
essentially caching the previously written print document and improving performance.
|
|
</p>
|
|
|
|
<p>The main work of {@link android.print.PrintDocumentAdapter#onLayout onLayout()} is
|
|
calculating the number of pages that are expected as output given the attributes of the printer.
|
|
How you calculate this number is highly dependent on how your application lays out pages for
|
|
printing. The following code example shows an implementation where the number of pages is
|
|
determined by the print orientation:</p>
|
|
|
|
<pre>
|
|
private int computePageCount(PrintAttributes printAttributes) {
|
|
int itemsPerPage = 4; // default item count for portrait mode
|
|
|
|
MediaSize pageSize = printAttributes.getMediaSize();
|
|
if (!pageSize.isPortrait()) {
|
|
// Six items per page in landscape orientation
|
|
itemsPerPage = 6;
|
|
}
|
|
|
|
// Determine number of print items
|
|
int printItemCount = getPrintItemCount();
|
|
|
|
return (int) Math.ceil(printItemCount / itemsPerPage);
|
|
}
|
|
</pre>
|
|
|
|
|
|
<h3 id="write-file">Write a print document file</h3>
|
|
|
|
<p>When it is time to write print output to a file, the Android print framework calls the {@link
|
|
android.print.PrintDocumentAdapter#onWrite onWrite()} method of your application's {@link
|
|
android.print.PrintDocumentAdapter} class. The method's parameters specify which pages should be
|
|
written and the output file to be used. Your implementation of this method must then render each
|
|
requested page of content to a multi-page PDF document file. When this process is complete, you
|
|
call the {@link android.print.PrintDocumentAdapter.WriteResultCallback#onWriteFinished
|
|
onWriteFinished()} method of the callback object.</p>
|
|
|
|
<p class="note">
|
|
<strong>Note:</strong> The Android print framework may call the {@link
|
|
android.print.PrintDocumentAdapter#onWrite onWrite()} method one or more times for every
|
|
call to {@link android.print.PrintDocumentAdapter#onLayout onLayout()}. For this reason, it is
|
|
important to set the boolean parameter of
|
|
{@link android.print.PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished
|
|
onLayoutFinished()} method to {@code false} when the print content layout has not changed,
|
|
to avoid unnecessary re-writes of the print document.
|
|
</p>
|
|
|
|
<p class="note">
|
|
<strong>Note:</strong> The boolean parameter of the
|
|
{@link android.print.PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished
|
|
onLayoutFinished()} method indicates whether or not the layout content has actually changed
|
|
since the last request. Setting this parameter properly allows the print framework to avoid
|
|
unnecessarily calling the {@link android.print.PrintDocumentAdapter#onLayout onLayout()} method,
|
|
essentially caching the previously written print document and improving performance.
|
|
</p>
|
|
|
|
|
|
<p>The following sample demonstrates the basic mechanics of this process using the {@link
|
|
android.print.pdf.PrintedPdfDocument} class to create a PDF file:</p>
|
|
|
|
<pre>
|
|
@Override
|
|
public void onWrite(final PageRange[] pageRanges,
|
|
final ParcelFileDescriptor destination,
|
|
final CancellationSignal cancellationSignal,
|
|
final WriteResultCallback callback) {
|
|
// Iterate over each page of the document,
|
|
// check if it's in the output range.
|
|
for (int i = 0; i < totalPages; i++) {
|
|
// Check to see if this page is in the output range.
|
|
if (containsPage(pageRanges, i)) {
|
|
// If so, add it to writtenPagesArray. writtenPagesArray.size()
|
|
// is used to compute the next output page index.
|
|
writtenPagesArray.append(writtenPagesArray.size(), i);
|
|
PdfDocument.Page page = mPdfDocument.startPage(i);
|
|
|
|
// check for cancellation
|
|
if (cancellationSignal.isCancelled()) {
|
|
callback.onWriteCancelled();
|
|
mPdfDocument.close();
|
|
mPdfDocument = null;
|
|
return;
|
|
}
|
|
|
|
// Draw page content for printing
|
|
drawPage(page);
|
|
|
|
// Rendering is complete, so page can be finalized.
|
|
mPdfDocument.finishPage(page);
|
|
}
|
|
}
|
|
|
|
// Write PDF document to file
|
|
try {
|
|
mPdfDocument.writeTo(new FileOutputStream(
|
|
destination.getFileDescriptor()));
|
|
} catch (IOException e) {
|
|
callback.onWriteFailed(e.toString());
|
|
return;
|
|
} finally {
|
|
mPdfDocument.close();
|
|
mPdfDocument = null;
|
|
}
|
|
PageRange[] writtenPages = computeWrittenPages();
|
|
// Signal the print framework the document is complete
|
|
callback.onWriteFinished(writtenPages);
|
|
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
<p>This sample delegates rendering of PDF page content to <code>drawPage()</code>
|
|
method, which is discussed in the next section.
|
|
</p>
|
|
|
|
<p>As with layout, execution of {@link android.print.PrintDocumentAdapter#onWrite onWrite()}
|
|
method can have three outcomes: completion, cancellation, or failure in the case where the
|
|
the content cannot be written. You must indicate one of these results by calling the
|
|
appropriate method of the {@link android.print.PrintDocumentAdapter.WriteResultCallback} object.
|
|
</p>
|
|
|
|
|
|
<p class="note">
|
|
<strong>Note:</strong> Rendering a document for printing can be a resource-intensive operation. In
|
|
order to avoid blocking the main user interface thread of your application, you should consider
|
|
performing the page rendering and writing operations on a separate thread, for example
|
|
in an {@link android.os.AsyncTask}.
|
|
For more information about working with execution threads like asynchronous tasks,
|
|
see <a href="{@docRoot}guide/components/processes-and-threads.html">Processes
|
|
and Threads</a>.
|
|
</p>
|
|
|
|
|
|
<h2 id="draw-content">Drawing PDF Page Content</h2>
|
|
|
|
<p>When your application prints, your application must generate a PDF document and pass it to
|
|
the Android print framework for printing. You can use any PDF generation library for this
|
|
purpose. This lesson shows how to use the {@link android.print.pdf.PrintedPdfDocument} class
|
|
to generate PDF pages from your content.</p>
|
|
|
|
<p>The {@link android.print.pdf.PrintedPdfDocument} class uses a {@link android.graphics.Canvas}
|
|
object to draw elements on an PDF page, similar to drawing on an activity layout. You can draw
|
|
elements on the printed page using the {@link android.graphics.Canvas} draw methods. The following
|
|
example code demonstrates how to draw some simple elements on a PDF document page using these
|
|
methods:</p>
|
|
|
|
<pre>
|
|
private void drawPage(PdfDocument.Page page) {
|
|
Canvas canvas = page.getCanvas();
|
|
|
|
// units are in points (1/72 of an inch)
|
|
int titleBaseLine = 72;
|
|
int leftMargin = 54;
|
|
|
|
Paint paint = new Paint();
|
|
paint.setColor(Color.BLACK);
|
|
paint.setTextSize(36);
|
|
canvas.drawText("Test Title", leftMargin, titleBaseLine, paint);
|
|
|
|
paint.setTextSize(11);
|
|
canvas.drawText("Test paragraph", leftMargin, titleBaseLine + 25, paint);
|
|
|
|
paint.setColor(Color.BLUE);
|
|
canvas.drawRect(100, 100, 172, 172, paint);
|
|
}
|
|
</pre>
|
|
|
|
<p>When using {@link android.graphics.Canvas} to draw on a PDF page, elements are specified in
|
|
points, which is 1/72 of an inch. Make sure you use this unit of measure for specifying the size
|
|
of elements on the page. For positioning of drawn elements, the coordinate system starts at 0,0
|
|
for the top left corner of the page.</p>
|
|
|
|
<p>
|
|
<strong>Tip:</strong> While the {@link android.graphics.Canvas} object allows you to place print
|
|
elements on the edge of a PDF document, many printers are not able to print to the edge of a
|
|
physical piece of paper. Make sure that you account for the unprintable edges of the page when
|
|
you build a print document with this class.
|
|
</p>
|