맞춤 문서 인쇄

그리기 앱, 페이지 레이아웃 앱, 텍스트, 이미지, 오디오, 동영상 등 아름답게 인쇄된 페이지를 만드는 것이 핵심 기능입니다. 이 경우에는 사용하여 이미지 또는 HTML 문서를 인쇄할 수 있습니다 이러한 유형의 응용 프로그램에 대한 인쇄 출력은 글꼴, 텍스트 흐름, 페이지 나누기, 헤더, 바닥글 및 그래픽 요소가 포함됩니다.

애플리케이션에 완전히 맞춤설정된 인쇄 출력을 만들려면 더 많은 작업이 필요합니다. 프로그래밍 투자의 비중을 줄일 수 있습니다. 빌드하려는 구성 요소를 인쇄 프레임워크와 통신하고, 프린터 설정에 맞게 조정하고, 페이지 요소를 그리고 인쇄 관리 기능을 제공합니다.

이 과정에서는 인쇄 관리자와 연결하고 인쇄 어댑터를 만들고 빌드 콘텐츠를 빌드합니다.

애플리케이션에서 인쇄 프로세스를 직접 관리하는 경우, 사용자의 print 요청은 Android 인쇄 프레임워크에 연결하여 인스턴스를 가져오는 것입니다. PrintManager 클래스의 하위 클래스입니다. 이 클래스를 사용하면 인쇄 작업을 초기화할 수 있습니다. 인쇄 수명 주기를 시작합니다 다음 코드 예는 인쇄 관리자를 가져오는 방법을 보여줍니다. 인쇄 프로세스를 시작합니다.

Kotlin

private fun doPrint() {
    activity?.also { context ->
        // Get a PrintManager instance
        val printManager = context.getSystemService(Context.PRINT_SERVICE) as PrintManager
        // Set job name, which will be displayed in the print queue
        val jobName = "${context.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, MyPrintDocumentAdapter(context), null)
    }
}

자바

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); //
}

위 코드 예에서는 인쇄 작업의 이름을 지정하고 인쇄 수명 주기 단계를 처리하는 PrintDocumentAdapter 클래스의 인스턴스를 설정하는 방법을 보여줍니다. 이 인쇄 어댑터 클래스의 구현은 다음 섹션에서 설명합니다.

참고: print()의 마지막 매개변수는 메서드는 PrintAttributes 객체를 사용합니다. 이 매개변수를 사용하여 이전 인쇄 주기에 따라 인쇄 프레임워크와 사전 설정된 옵션에 대한 힌트를 제공합니다. 사용자 환경을 개선할 수 있습니다 또한 이 매개변수를 사용하여 방향을 가로로 설정하는 등 인쇄되는 콘텐츠에 더 적합할 수 있습니다. 사진을 인쇄할 때 사용됩니다.

인쇄 어댑터는 Android 인쇄 프레임워크와 상호작용하고 몇 가지 단계를 수행했습니다. 이 과정에서 사용자는 문서를 만들기 전에 프린터와 인쇄 옵션을 선택해야 합니다. 인쇄용 문서가 필요합니다. 이러한 선택은 사용자가 선택하는 최종 출력에 영향을 줄 수 있습니다. 다른 인쇄 기능, 페이지 크기 또는 페이지 방향이 다른 프린터. 이러한 선택이 이루어지면 인쇄 프레임워크는 최신 버전의 레이어를 배치하고 생성하도록 어댑터에 출력 문서를 생성합니다. 사용자가 인쇄 버튼을 탭하면 프레임워크는 는 최종 인쇄 문서를 가져와서 출력을 위해 인쇄 제공자에 전달합니다. 인쇄 중 프로세스에서 사용자가 인쇄 작업을 취소하도록 선택할 수 있으므로 인쇄 어댑터는 취소 요청에 대응할 수 있습니다.

PrintDocumentAdapter 추상 클래스는 네 가지 기본 콜백 메서드가 있는 인쇄 수명 주기입니다. 이러한 메서드를 구현해야 합니다. 설정해야 합니다.

  • onStart() - 확인할 수 있습니다. 애플리케이션에 일회성 준비 작업이 있고 예를 들어 출력할 데이터의 스냅샷을 가져오는 경우, 여기에서 이를 실행합니다. 구현 어댑터의 이 메서드는 필요하지 않습니다.
  • onLayout() - 사용자가 페이지 크기 변경, 또는 페이지 방향이므로 애플리케이션에서 요소의 레이아웃을 계산할 기회를 인쇄되어야 합니다. 이 메서드는 최소한 예상 페이지 수를 반환해야 합니다. 출력된 문서에 표시됩니다.
  • onWrite() - 인쇄를 렌더링하기 위해 호출됩니다. 출력됩니다. 이 메서드는 각 호출 후 한 번 이상 호출될 수 있습니다. onLayout() 호출
  • onFinish() - 마지막에 한 번 호출됩니다. 몇 가지 단계를 수행했습니다. 애플리케이션에 수행할 일회성 해체 작업이 있는 경우 여기에서 실행해 보세요 어댑터에서 이 메서드를 구현하지 않아도 됩니다.

다음 섹션에서는 레이아웃을 구현하고 메서드를 작성하는 방법을 설명합니다. 이는 인쇄 어댑터의 작동에 매우 중요합니다.

참고: 이러한 어댑터 메서드는 애플리케이션의 기본 스레드에서 호출됩니다. 만약 구현에서 이러한 메서드를 실행하는 데 별도의 스레드 내에서 실행되도록 구현합니다. 예를 들어 별도의 AsyncTask 객체에서 레이아웃 또는 인쇄 문서 작성 작업을 수행합니다.

인쇄 문서 정보 계산

PrintDocumentAdapter 클래스의 구현 내에서 애플리케이션은 만들고 있는 문서의 유형을 지정하고 총 인쇄된 페이지 크기에 대한 정보를 고려하여 인쇄 작업을 위한 페이지 수입니다. onLayout() 메서드의 구현은 어댑터는 이러한 계산을 수행하고 PrintDocumentInfo 클래스의 인쇄 작업(페이지 수와 있습니다. 다음 코드 예에서는 PrintDocumentAdapteronLayout() 메서드에 관한 기본 구현을 보여줍니다.

Kotlin

override fun onLayout(
        oldAttributes: PrintAttributes?,
        newAttributes: PrintAttributes,
        cancellationSignal: CancellationSignal?,
        callback: LayoutResultCallback,
        extras: Bundle?
) {
    // Create a new PdfDocument with the requested page attributes
    pdfDocument = PrintedPdfDocument(activity, newAttributes)

    // Respond to cancellation request
    if (cancellationSignal?.isCanceled == true) {
        callback.onLayoutCancelled()
        return
    }

    // Compute the expected number of printed pages
    val pages = computePageCount(newAttributes)

    if (pages > 0) {
        // Return print information to print framework
        PrintDocumentInfo.Builder("print_output.pdf")
                .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                .setPageCount(pages)
                .build()
                .also { info ->
                    // Content layout reflow is complete
                    callback.onLayoutFinished(info, true)
                }
    } else {
        // Otherwise report an error to the print framework
        callback.onLayoutFailed("Page count calculation failed.")
    }
}

자바

@Override
public void onLayout(PrintAttributes oldAttributes,
                     PrintAttributes newAttributes,
                     CancellationSignal cancellationSignal,
                     LayoutResultCallback callback,
                     Bundle metadata) {
    // Create a new PdfDocument with the requested page attributes
    pdfDocument = new PrintedPdfDocument(getActivity(), newAttributes);

    // Respond to cancellation request
    if (cancellationSignal.isCanceled() ) {
        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.");
    }
}

onLayout() 메서드의 실행은 다음을 수행할 수 있습니다. 세 가지 결과가 나오는데, 이는 일정 계산 시 완료, 취소, 실패 레이아웃을 완료할 수 없습니다. 적절한 PrintDocumentAdapter.LayoutResultCallback 객체의 메서드를 기반으로 합니다.

참고: onLayoutFinished() 메서드는 레이아웃 콘텐츠가 실제로 변경되었는지 여부를 나타냅니다. 확인할 수 있습니다. 이 매개변수를 적절히 설정하면 인쇄 프레임워크에서 onWrite() 메서드를 불필요하게 호출하는 경우 근본적으로 이전에 작성된 인쇄 문서를 캐시하고 성능을 개선합니다.

onLayout()의 주요 작업은 다음과 같습니다. 프린터의 속성을 고려하여 예상되는 출력 페이지 수를 계산합니다. 이 숫자를 계산하는 방법은 애플리케이션이 있습니다. 다음 코드 예는 페이지 수가 다음과 같이 인쇄 방향에 따라 결정됩니다.

Kotlin

private fun computePageCount(printAttributes: PrintAttributes): Int {
    var itemsPerPage = 4 // default item count for portrait mode

    val pageSize = printAttributes.mediaSize
    if (!pageSize.isPortrait) {
        // Six items per page in landscape orientation
        itemsPerPage = 6
    }

    // Determine number of print items
    val printItemCount: Int = getPrintItemCount()

    return Math.ceil((printItemCount / itemsPerPage.toDouble())).toInt()
}

자바

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);
}

인쇄 문서 파일 작성

인쇄 출력을 파일에 작성할 때가 되면 Android 인쇄 프레임워크는 애플리케이션 PrintDocumentAdapter 클래스의 onWrite() 메서드를 호출합니다. 이 메서드의 매개변수는 출력 파일을 정의합니다. 그런 다음 이 메서드의 구현은 각 객체를 렌더링해야 합니다. 다중 페이지 PDF 문서 파일로 변환할 수 있습니다. 이 프로세스가 완료되면 콜백 객체의 onWriteFinished() 메서드를 호출합니다.

참고: Android 인쇄 프레임워크는 메시지마다 onWrite() 메서드를 한 번 이상 호출할 수 있습니다. onLayout()를 호출합니다. 따라서 인코더-디코더 모델의 onLayoutFinished() 메서드를 false로 전달하면 사용하여 인쇄 문서를 불필요하게 다시 쓰지 않아도 됩니다.

참고: onLayoutFinished() 메서드는 레이아웃 콘텐츠가 실제로 변경되었는지 여부를 나타냅니다. 확인할 수 있습니다. 이 매개변수를 적절히 설정하면 인쇄 프레임워크에서 onLayout() 메서드를 불필요하게 호출하는 경우 근본적으로 이전에 작성된 인쇄 문서를 캐시하고 성능을 개선합니다.

다음 샘플에서는 PrintedPdfDocument 클래스를 사용하여 PDF 파일을 만드는 이 프로세스의 기본 메커니즘을 보여줍니다.

Kotlin

override fun onWrite(
        pageRanges: Array<out PageRange>,
        destination: ParcelFileDescriptor,
        cancellationSignal: CancellationSignal?,
        callback: WriteResultCallback
) {
    // Iterate over each page of the document,
    // check if it's in the output range.
    for (i in 0 until totalPages) {
        // 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?.startPage(i)?.also { page ->

                // check for cancellation
                if (cancellationSignal?.isCanceled == true) {
                    callback.onWriteCancelled()
                    pdfDocument?.close()
                    pdfDocument = null
                    return
                }

                // Draw page content for printing
                drawPage(page)

                // Rendering is complete, so page can be finalized.
                pdfDocument?.finishPage(page)
            }
        }
    }

    // Write PDF document to file
    try {
        pdfDocument?.writeTo(FileOutputStream(destination.fileDescriptor))
    } catch (e: IOException) {
        callback.onWriteFailed(e.toString())
        return
    } finally {
        pdfDocument?.close()
        pdfDocument = null
    }
    val writtenPages = computeWrittenPages()
    // Signal the print framework the document is complete
    callback.onWriteFinished(writtenPages)

    ...
}

자바

@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 = pdfDocument.startPage(i);

            // check for cancellation
            if (cancellationSignal.isCanceled()) {
                callback.onWriteCancelled();
                pdfDocument.close();
                pdfDocument = null;
                return;
            }

            // Draw page content for printing
            drawPage(page);

            // Rendering is complete, so page can be finalized.
            pdfDocument.finishPage(page);
        }
    }

    // Write PDF document to file
    try {
        pdfDocument.writeTo(new FileOutputStream(
                destination.getFileDescriptor()));
    } catch (IOException e) {
        callback.onWriteFailed(e.toString());
        return;
    } finally {
        pdfDocument.close();
        pdfDocument = null;
    }
    PageRange[] writtenPages = computeWrittenPages();
    // Signal the print framework the document is complete
    callback.onWriteFinished(writtenPages);

    ...
}

이 샘플에서는 PDF 페이지 콘텐츠 렌더링을 drawPage()에 위임합니다. 메서드에 대한 자세한 내용은 다음 섹션에서 설명합니다.

레이아웃과 마찬가지로 onWrite()의 실행 메서드는 완료, 취소, 실패와 같은 세 가지 결과가 나올 수 있습니다. 콘텐츠를 쓸 수 없습니다. 이 결과 중 하나를 표시하려면 PrintDocumentAdapter.WriteResultCallback 객체의 적절한 메서드를 사용해야 합니다.

참고: 인쇄할 문서를 렌더링하는 것은 리소스를 많이 사용하는 작업일 수 있습니다. 포함 애플리케이션의 기본 사용자 인터페이스 스레드를 차단하지 않으려면 별도의 스레드에서 페이지 렌더링 및 작성 작업 수행(예: AsyncTask에 포함 비동기 작업과 같은 실행 스레드 사용에 관한 자세한 내용은 프로세스 스레드를 지원합니다.

PDF 페이지 콘텐츠 그리기

애플리케이션이 인쇄되면 애플리케이션은 PDF 문서를 생성하여 인쇄용 Android 인쇄 프레임워크입니다. 이 작업에는 모든 PDF 생성 라이브러리를 사용할 수 있습니다. 있습니다. 이 과정에서는 PrintedPdfDocument 클래스를 사용하는 방법을 보여줍니다. 콘텐츠에서 PDF 페이지를 생성할 수 있습니다

PrintedPdfDocument 클래스는 Canvas를 사용합니다. 객체를 사용하여 PDF 페이지에 요소를 그릴 수 있습니다. 이는 활동 레이아웃에 그리는 것과 비슷합니다. 그림을 그릴 수 있습니다. Canvas 그리기 메서드를 사용하여 인쇄된 페이지에 요소를 추가합니다. 다음 예시 코드는 메서드:

Kotlin

private fun drawPage(page: PdfDocument.Page) {
    page.canvas.apply {

        // units are in points (1/72 of an inch)
        val titleBaseLine = 72f
        val leftMargin = 54f

        val paint = Paint()
        paint.color = Color.BLACK
        paint.textSize = 36f
        drawText("Test Title", leftMargin, titleBaseLine, paint)

        paint.textSize = 11f
        drawText("Test paragraph", leftMargin, titleBaseLine + 25, paint)

        paint.color = Color.BLUE
        drawRect(100f, 100f, 172f, 172f, paint)
    }
}

자바

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);
}

Canvas를 사용하여 PDF 페이지에 그릴 때 요소는 포인트는 1/72인치입니다. 이 측정 단위를 사용하여 크기를 지정해야 합니다. 인코더에 전달합니다. 그려진 요소의 위치를 지정하기 위해 좌표계는 0,0에서 시작합니다. 을 터치하세요.

도움말: Canvas 객체를 사용하면 인쇄물을 배치할 수 있지만 여러 프린터가 PDF 문서 가장자리에 인쇄되지 않는 경우 만듭니다. 페이지를 닫을 때 인쇄할 수 없는 가장자리를 고려하여 이 클래스로 인쇄 문서를 빌드합니다.