当前位置: 首页 > ops >正文

Andorid 文件打印功能

最近需要的需求需要Android调用系统打印机,并打印文件,类似如下的功能:

Android 调用打印机

实现的逻辑如下:
因为我们这个是 pdf 文件,首先需要将 pdf转化为 Bitmap 位图,然后向调用打印机传递位图即可。

首先我们需要创建 pdf 转 Bitmap 功能:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.pdf.PdfDocument;
import android.graphics.pdf.PdfRenderer;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;import android.os.CancellationSignal;
import android.print.PrintDocumentInfo;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;public class BitmapPrintAdapter extends PrintDocumentAdapter {private Context context;private final String pdfPath;private PdfRenderer pdfRenderer;private PrintAttributes printAttributes;private final List<Bitmap> pagesBitmap = new ArrayList<>();public BitmapPrintAdapter(Context context, String pdfPath) {this.context = context;this.pdfPath = pdfPath;}@Overridepublic void onLayout(PrintAttributes oldAttributes,PrintAttributes newAttributes,CancellationSignal cancellationSignal,LayoutResultCallback callback,Bundle extras) {if (cancellationSignal.isCanceled()) {callback.onLayoutCancelled();return;}try {// 释放之前可能存在的资源if (pdfRenderer != null) {for (Bitmap bitmap : pagesBitmap) {if (bitmap != null && !bitmap.isRecycled()) {bitmap.recycle();}}pagesBitmap.clear();pdfRenderer.close();}// 打开PDF文件ParcelFileDescriptor fileDescriptor =ParcelFileDescriptor.open(new File(pdfPath), ParcelFileDescriptor.MODE_READ_ONLY);pdfRenderer = new PdfRenderer(fileDescriptor);// 检查PDF第一页的宽高比来确定方向PdfRenderer.Page firstPage = pdfRenderer.openPage(0);int pdfWidth = firstPage.getWidth();int pdfHeight = firstPage.getHeight();firstPage.close();// 如果宽度大于高度,则为横向// 记录PDF是否为横向boolean isLandscape = pdfWidth > pdfHeight;// 创建新的打印属性,并设置正确的方向PrintAttributes.Builder builder = new PrintAttributes.Builder();if (null != newAttributes){if (null != newAttributes.getMediaSize()){builder.setMediaSize(newAttributes.getMediaSize());}builder.setColorMode(newAttributes.getColorMode());if (null != newAttributes.getResolution()){builder.setResolution(newAttributes.getResolution());}if (null != newAttributes.getMinMargins()){builder.setMinMargins(newAttributes.getMinMargins());}builder.setDuplexMode(newAttributes.getDuplexMode());}if (isLandscape) {// 横向A4builder.setMediaSize(PrintAttributes.MediaSize.ISO_A4.asLandscape());} else {// 纵向A4builder.setMediaSize(PrintAttributes.MediaSize.ISO_A4);}this.printAttributes = builder.build();int pageCount = pdfRenderer.getPageCount();// 对每一页进行预渲染for (int i = 0; i < pageCount; i++) {PdfRenderer.Page page = pdfRenderer.openPage(i);// 创建适合打印尺寸的位图,保持原始比例int pageWidth = page.getWidth();int pageHeight = page.getHeight();// 提高分辨率float scale = 2.5f;int bitmapWidth = (int) (pageWidth * scale);int bitmapHeight = (int) (pageHeight * scale);Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);bitmap.eraseColor(Color.WHITE);  // 白色背景// 渲染PDF页面到位图page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT);pagesBitmap.add(bitmap);page.close();}// 使用正确的方向信息创建文档信息PrintDocumentInfo.Builder infoBuilder = new PrintDocumentInfo.Builder("print_bitmap.pdf").setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(pageCount);PrintDocumentInfo info = infoBuilder.build();// 通知布局完成,并指示打印参数已更改callback.onLayoutFinished(info, newAttributes!= null && !newAttributes.equals(this.printAttributes));} catch (IOException e) {CrashReport.postCatchedException(new RuntimeException("bitmapCreate", e));callback.onLayoutFailed("无法渲染PDF: " + e.getMessage());}}@Overridepublic void onWrite(PageRange[] pageRanges, ParcelFileDescriptor destination,CancellationSignal cancellationSignal,WriteResultCallback callback) {if (cancellationSignal.isCanceled() || pagesBitmap.isEmpty()) {callback.onWriteCancelled();return;}try {// 创建PDF文档输出PdfDocument pdfDocument = new PdfDocument();int totalPages = pagesBitmap.size();// 获取需要打印的实际页面List<Integer> writtenPages = new ArrayList<>();for (PageRange range : pageRanges) {final int start = range.getStart();final int end = range.getEnd();for (int i = start; i <= end && i < totalPages; i++) {writtenPages.add(i);}}// 创建每一页for (int i : writtenPages) {if (cancellationSignal.isCanceled()) {callback.onWriteCancelled();pdfDocument.close();return;}Bitmap bitmap = pagesBitmap.get(i);// 使用位图实际尺寸创建页面信息,确保保持原始方向PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(bitmap.getWidth(),bitmap.getHeight(),i).create();PdfDocument.Page page = pdfDocument.startPage(pageInfo);// 在页面画布上绘制位图Canvas canvas = page.getCanvas();canvas.drawBitmap(bitmap, 0, 0, null);pdfDocument.finishPage(page);}// 写入PDF文档到输出流pdfDocument.writeTo(new FileOutputStream(destination.getFileDescriptor()));pdfDocument.close();// 构建实际打印的页面范围PageRange[] actualPages = new PageRange[writtenPages.size()];for (int i = 0; i < writtenPages.size(); i++) {int page = writtenPages.get(i);actualPages[i] = new PageRange(page, page);}callback.onWriteFinished(actualPages);} catch (IOException e) {           callback.onWriteFailed("打印失败: " + e.getMessage());}}@Overridepublic void onFinish() {// 释放资源if (pdfRenderer != null) {pdfRenderer.close();}for (Bitmap bitmap : pagesBitmap) {if (bitmap != null && !bitmap.isRecycled()) {bitmap.recycle();}}pagesBitmap.clear();}}

创建完 Adapter 之后,调用系统代码就比较简单了:

public void printPDF(String savePDFFilePath){PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);PrintAttributes.MediaSize mediaSize = PrintAttributes.MediaSize.ISO_A4;if(detectPdfOrientationIsLandscape()){mediaSize = PrintAttributes.MediaSize.ISO_A4.asLandscape();}// 创建初始打印属性PrintAttributes printAttributes = new PrintAttributes.Builder()// 让打印适配器自动检测方向.setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME).setResolution(new PrintAttributes.Resolution("pdf", "PDF Document", 300, 300)).setMinMargins(PrintAttributes.Margins.NO_MARGINS).setMediaSize(mediaSize).build();printManager.print("BitmapPrintJob_" + System.currentTimeMillis(),new BitmapPrintAdapter(this, savePDFFilePath),printAttributes);}

其中 detectPdfOrientationIsLandscape()为:

private boolean detectPdfOrientationIsLandscape()  {ParcelFileDescriptor fileDescriptor = null ;try {fileDescriptor  =ParcelFileDescriptor.open(new File(savePDFFilePath), ParcelFileDescriptor.MODE_READ_ONLY);PdfRenderer renderer = new PdfRenderer(fileDescriptor);if (renderer.getPageCount() > 0) {PdfRenderer.Page page = renderer.openPage(0);int width = page.getWidth();int height = page.getHeight();BLog.d("PDF Orientation detection: Width=" + width + ", Height=" + height + ", isLandscape=" + (width > height));page.close();return  width > height;}renderer.close();fileDescriptor.close();}catch (IOException e){e.printStackTrace();}finally {FileUtils.INSTANCE.closeQuite(fileDescriptor);}return false;}

OK.

http://www.xdnf.cn/news/4625.html

相关文章:

  • React 实现 JWT 登录验证的最小可运行示例
  • 计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 05.纹理贴图
  • Ubuntu 服务器管理命令笔记
  • 系统重装之后,通过ssh无法登录
  • 安卓基础(XML)
  • Vue2 中 el-dialog 封装组件属性不生效的深度解析(附 $attrs、inheritAttrs 原理)
  • DApp开发:开启去中心化应用新时代
  • LLaMA模型本地部署全攻略:从零搭建私有化AI助手
  • Algolia - Docsearch的申请配置安装【以踩坑解决版】
  • 2025年渗透测试面试题总结-某步在线面试(题目+回答)
  • 枚举 · 例8扩展-校门外的树:hard
  • 2025年APP安全攻防指南:抵御DDoS与CC攻击的实战策略
  • 神经网络—感知器、多层感知器
  • matlab实现模型预测控制
  • Qt/C++面试【速通笔记八】—Qt的事件处理机制
  • Solidity语言基础:区块链智能合约开发入门指南
  • 软件设计师教程——第一章 计算机系统知识(上)
  • tmux 入门与实用指南
  • 从零开始用 AI 编写一个复杂项目的实践方法论
  • R语言数据挖掘:从“挖井”到“淘金”
  • C31-形参与实参的区别
  • Google 发布 Gemini 2.5 Pro Preview (I/O Edition),具有增强的编程能力
  • 多模态文档检索开源方案-三大竞赛获奖方案技术链路
  • Flink SQL DataStream 融合开发模式与动态配置热加载机制实战
  • C++ STL 入门:map 键值对容器
  • Centos离线安装mysql、redis、nginx等工具缺乏层层依赖的解决方案
  • 全面解析 iTextSharp:在 .NET 中高效处理 PDF
  • 贵州安全员考试内容有哪些?
  • Python学习笔记--Django的安装和简单使用(一)
  • 【Linux网络】Socket 编程预备