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

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.

相关文章:

  • 服务器托管的常见问题
  • c语言与c++到底有什么区别?
  • 【STM32单片机】#15 WDG看门狗
  • pygame联网飞机大战游戏实现
  • Elastic:什么是 AIOps?
  • ROP链-BUUCTF-cmcc_simplerop(ret2syscall)
  • vue v-html无法解析<
  • Linux网络编程day7 线程池and UDP
  • 只出现一次的数字(暴力、哈希查重、异或运算)
  • 交流中的收获-250508
  • 云手机虚拟地址技术的运营场景
  • FreeRTOS如何检测内存泄漏
  • ConcurrentHashMap解析
  • Java高频面试之并发编程-14
  • 设备管理系统深度测评:如何用 AI 知识库实现故障智能诊断?
  • 沃伦森电容器支路阻抗特性监控系统 电容器组智能健康管理专家
  • 模拟内存管理
  • 工程管理系统简介 工程管理系统源码 java工程管理系统 工程管理系统功能设计 从架构到实操
  • 若依框架Ruoyi-vue整合图表Echarts中国地图标注动态数据
  • TCP/IP和OSI对比
  • 悬疑推理联合书单|虫神山事件
  • 国家发改委:美芯片药品等领域关税影响全球科技发展,损害人类共同利益
  • 长三角地区中华老字号品牌景气指数发布,哪些牌子是你熟悉的?
  • 保证断电、碰撞等事故中车门系统能够开启!隐藏式门把手将迎来强制性国家标准
  • 中华人民共和国和俄罗斯联邦关于进一步加强合作维护国际法权威的联合声明
  • 比尔·盖茨:未来20年通过盖茨基金会捐出几乎全部财富,2045年底基金会停止运营