You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PrintDocumentAdapter生成PDF右侧出现黑边问题求助

解决PrintDocumentAdapter打印ScrollView内容时PDF右侧出现黑边的问题

问题描述

使用PrintDocumentAdapter打印ScrollView内的界面内容时,生成的PDF文档右侧存在黑边,但实际界面并无该空白区域。调整canvas的x轴索引后问题依然存在。

原实现代码

public void printScrollViewContent() {
  PrintManager printManager = (PrintManager) getContext().getSystemService(Context.PRINT_SERVICE);
  String jobName = getString(R.string.app_name) + " Document";

  printManager.print(jobName, new PrintDocumentAdapter() {
    @Override
    public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
      if (cancellationSignal.isCanceled()) {
        callback.onLayoutCancelled();
        return;
      }

      // Get the dimensions of the scroll view
      int height = scrollView.getHeight();

      // Get the total size of the content in the scroll view
      int totalHeight = scrollView.getChildAt(0).getHeight();

      // Calculate the number of pages needed
      numberOfPages = (int) Math.ceil((double) totalHeight / height);

      Rect pageDimensions = new Rect();
      scrollView.getDrawingRect(pageDimensions);

      // Prepare the PDF document layout
      PrintDocumentInfo.Builder builder = new PrintDocumentInfo.Builder("print_output.pdf");
      builder.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
          .setPageCount(numberOfPages);

      PrintDocumentInfo info = builder.build();
      callback.onLayoutFinished(info, true);
    }

    @Override
    public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) {
      // Render content and write to PDF document
      PdfDocument pdfDocument = new PdfDocument();
      for (int i = 0; i < numberOfPages; i++) {
        PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(scrollView.getWidth(), scrollView.getHeight(), i).create();
        PdfDocument.Page page = pdfDocument.startPage(pageInfo);
        Canvas canvas = page.getCanvas();

        // Calculate the portion of the content to be drawn for this page
        int start = i * canvas.getHeight();
        int end = Math.min((i + 1) * canvas.getHeight(), scrollView.getChildAt(0).getHeight());

        // Create bitmap of the portion of the scroll view content for this page
        Bitmap bitmap = Bitmap.createBitmap(scrollView.getWidth(), end - start, Bitmap.Config.ARGB_8888);

        Canvas contentCanvas = new Canvas(bitmap);
        contentCanvas.translate(0, -start); // Translate canvas to the appropriate position

        scrollView.getChildAt(0).draw(contentCanvas);

        // Draw the bitmap onto the canvas
        canvas.drawBitmap(bitmap, 0, 0, null);

        // Finish the page
        pdfDocument.finishPage(page);
      }

      try {
        // Write the PDF document to the output stream
        pdfDocument.writeTo(new FileOutputStream(destination.getFileDescriptor()));
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        pdfDocument.close();
      }

      callback.onWriteFinished(new PageRange[]{new PageRange(0, numberOfPages - 1)});
    }
  }, null);
}

问题根源

  1. 页面尺寸不匹配:原代码直接使用ScrollView的屏幕宽高创建PDF页面,但打印系统的纸张尺寸(如A4)宽高比与屏幕宽高比不一致,导致内容无法填满打印页面宽度,出现右侧空白/黑边。
  2. 内容宽度获取错误:使用scrollView.getWidth()作为内容宽度,而实际内容宽度应为ScrollView子View的真实宽度(scrollView.getChildAt(0).getWidth()),当子View宽度超过ScrollView时会导致内容截断或空白。

修复后的代码

public void printScrollViewContent() {
    PrintManager printManager = (PrintManager) getContext().getSystemService(Context.PRINT_SERVICE);
    String jobName = getString(R.string.app_name) + " Document";

    printManager.print(jobName, new PrintDocumentAdapter() {
        private int numberOfPages;
        private int contentWidth;
        private int contentTotalHeight;

        @Override
        public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
            if (cancellationSignal.isCanceled()) {
                callback.onLayoutCancelled();
                return;
            }

            // 获取ScrollView子View的真实内容尺寸
            View contentView = scrollView.getChildAt(0);
            contentWidth = contentView.getWidth();
            contentTotalHeight = contentView.getHeight();

            // 将打印纸张尺寸转换为PDF使用的点单位(1英寸=72点)
            PrintAttributes.MediaSize mediaSize = newAttributes.getMediaSize();
            int pageWidth = mediaSize.getWidthMils() * 72 / 1000;
            int pageHeight = mediaSize.getHeightMils() * 72 / 1000;

            // 计算缩放比例,让内容宽度完全适配打印页面宽度
            float scale = (float) pageWidth / contentWidth;
            int scaledTotalHeight = (int) (contentTotalHeight * scale);

            // 计算需要的打印页数
            numberOfPages = (int) Math.ceil((double) scaledTotalHeight / pageHeight);

            // 构建打印文档信息
            PrintDocumentInfo info = new PrintDocumentInfo.Builder("print_output.pdf")
                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                    .setPageCount(numberOfPages)
                    .build();

            callback.onLayoutFinished(info, true);
        }

        @Override
        public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) {
            if (cancellationSignal.isCanceled()) {
                callback.onWriteCancelled();
                return;
            }

            PdfDocument pdfDocument = new PdfDocument();
            View contentView = scrollView.getChildAt(0);
            PrintAttributes.MediaSize mediaSize = new PrintAttributes.Builder().build().getMediaSize();
            int pageWidth = mediaSize.getWidthMils() * 72 / 1000;
            int pageHeight = mediaSize.getHeightMils() * 72 / 1000;
            float scale = (float) pageWidth / contentWidth;

            for (int i = 0; i < numberOfPages; i++) {
                // 创建适配纸张尺寸的PDF页面
                PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(pageWidth, pageHeight, i + 1).create();
                PdfDocument.Page page = pdfDocument.startPage(pageInfo);
                Canvas canvas = page.getCanvas();

                // 计算当前页对应的内容起始位置(基于原始内容高度)
                int startY = (int) (i * pageHeight / scale);
                int endY = Math.min((int) ((i + 1) * pageHeight / scale), contentTotalHeight);
                int contentHeight = endY - startY;

                // 创建对应内容区域的Bitmap
                Bitmap bitmap = Bitmap.createBitmap(contentWidth, contentHeight, Bitmap.Config.ARGB_8888);
                Canvas contentCanvas = new Canvas(bitmap);
                contentCanvas.translate(0, -startY); // 偏移到当前页的内容位置
                contentView.draw(contentCanvas);

                // 缩放Bitmap至打印页面宽度,避免右侧空白
                canvas.save();
                canvas.scale(scale, scale);
                canvas.drawBitmap(bitmap, 0, startY, null);
                canvas.restore();

                pdfDocument.finishPage(page);
            }

            try {
                pdfDocument.writeTo(new FileOutputStream(destination.getFileDescriptor()));
            } catch (IOException e) {
                e.printStackTrace();
                callback.onWriteFailed(e.getMessage());
                return;
            } finally {
                pdfDocument.close();
            }

            callback.onWriteFinished(new PageRange[]{new PageRange(0, numberOfPages - 1)});
        }
    }, null);
}

关键修改说明

  • 适配打印纸张尺寸:获取打印属性中的纸张尺寸,转换为PDF标准的点单位,确保页面尺寸符合打印系统要求。
  • 内容缩放适配:计算内容与打印页面的缩放比例,让内容宽度完全匹配打印页面宽度,消除右侧空白/黑边。
  • 正确计算分页:基于缩放后的高度计算分页,保证每页内容完整。
  • 真实内容尺寸:使用ScrollView子View的真实宽高作为内容尺寸,避免因ScrollView裁剪导致的内容错误。

内容的提问来源于stack exchange,提问作者user23515317

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:54:52