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

Android 生成pdf文件

Android 生成pdf文件

1.使用官方的方式

使用官方的方式也就是PdfDocument类的使用

1.1 基本使用

/**** 将tv内容写入到pdf文件*/@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void newPdf() {// 创建一个PDF文本对象PdfDocument document = new PdfDocument();//创建当前页的信息,Builder中的参数表示页面的宽高,以及第几页
​PdfDocument.PageInfo pageInfo =new PdfDocument.PageInfo.Builder(binding.pdfTv.getWidth(), binding.pdfTv.getHeight(), 1).create();// 生成当前页PdfDocument.Page page = document.startPage(pageInfo);
​// 在当前页上画画,即把所需要的view的视图画到page的画布上View content = pdfTv;content.draw(page.getCanvas());
​// 结束当前页document.finishPage(page);String filePath = getExternalFilesDir("").getAbsolutePath() + "/test.pdf";try {FileOutputStream outputStream = new FileOutputStream(new File(filePath));//写入到文件中document.writeTo(outputStream);} catch (IOException e) {e.printStackTrace();}//关闭document.close();}

注意事项

1.需要申请写入文件的权限

2.API最低是19,有api版本的限制

1.2 将根布局的内容生成pdf文件

    /**** 保存一个屏幕的内容(包含界面中的 文字、图片、按钮等)*/@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void pdfTvAndIv() {// 创建一个PDF文本对象PdfDocument document = new PdfDocument();//创建当前页的信息,Builder中的参数表示页面的宽高,以及第几页
​PdfDocument.PageInfo pageInfo =new PdfDocument.PageInfo.Builder(binding.getRoot().getWidth(), binding.getRoot().getHeight(), 1).create();// 生成当前页PdfDocument.Page page = document.startPage(pageInfo);
​// 在当前页上画画,即把所需要的view的视图画到page的画布上View content = binding.getRoot();content.draw(page.getCanvas());
​// 结束当前页document.finishPage(page);String filePath = getExternalFilesDir("").getAbsolutePath() + "/tviv.pdf";try {FileOutputStream outputStream = new FileOutputStream(new File(filePath));document.writeTo(outputStream);} catch (IOException e) {e.printStackTrace();}
​document.close();}

也同样简单。binding.getRoot()就是xml文件的根布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools">
​<data>
​</data>
​<ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".pdf.PdfActivity">
​<LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical">
​<TextViewandroid:id="@+id/pdf_tv"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="" />
​<ImageViewandroid:id="@+id/pdf_iv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:scaleType="fitXY"android:src="@mipmap/logo"android:visibility="visible" />
​<Buttonandroid:id="@+id/pdf1"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="官方方式生成pdf" /></LinearLayout>
​</ScrollView>
</layout>

1.3 TextView有很多行,超过一屏

/**** 将多行的文字写入到pdf文件*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void pdfInterviewContent() {TextView tv_content = binding.pdfTv;// 创建一个PDF文本对象PdfDocument document = new PdfDocument();// 一页pdf的高度int onePageHeight = tv_content.getLineHeight() * 30;// TextView中总共有多少行int lineCount = tv_content.getLineCount();// 计算这个TextView需要分成多少页int pdfCount = lineCount % 30 == 0 ? lineCount / 30 : lineCount / 30 + 1;for (int i = 0; i < pdfCount; i++) {PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(tv_content.getWidth(), onePageHeight + 120, 1).setContentRect(new Rect(0, 60, tv_content.getWidth(), onePageHeight + 60)).create();PdfDocument.Page page = document.startPage(pageInfo);Canvas canvas = page.getCanvas();canvas.translate(0, -onePageHeight * i);tv_content.draw(canvas);document.finishPage(page);}//document = pdfImageviewContent(document);File file = new File(getExternalFilesDir("").getAbsolutePath() + "/test.pdf");try {FileOutputStream outputStream = new FileOutputStream(file);document.writeTo(outputStream);} catch (IOException e) {e.printStackTrace();}document.close();
}

1.4 小结

1.保存的文件有些大
2.保存超过一屏的内容有些难,涉及到canvas的移动
3.同时保存文字与图片有些难度(如果超过一屏幕的话,图片保存后会是空白的情况。没有超过一屏,保存后能正常显示)
4.如果界面中有特殊的view,可能会保存失败!比如说SurfaceView。
[附上解决方案的链接](https://www.jianshu.com/p/1ebaf5e6fac1)

2.使用itext的方式

对于Itext,主要有两个版本,一个是5.x,另一个是7.x,这两个版本是完全是不兼容的,其区别可以参考官网:iText 7 and iText 5: roadmaps, differences, updates | iText PDF,

5.x的文档:iText Javadoc Home

7.x的文档:iText Javadoc Home

2.1 7.x

一、引入依赖

//iText 7.0+
implementation 'com.itextpdf:itext7-core:7.1.13'

目前使用的Android Studio版本是4.0.1,我使用的是jdk1.8,不能引入最新的7.1.15版本,会报错

Unsupported class file major version 59

这个问题对jdk有要求的。

二、申请权限

清单文件AndroidManifest.xml

2.1 申请权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

2.2 在<application>中添加

android:requestLegacyExternalStorage="true"

2.3 添加provider

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.example.demo.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
         android:name="android.support.FILE_PROVIDER_PATHS"
         android:resource="@xml/provider_paths" />
</provider>

2.4 在res目录下新建xml目录,并创建provider_paths.xml文件

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
</paths>

三、创建pdf文件

/*** 创建PDF文件*/
private void createPDF(String path) {if (XXPermissions.isGranted(TextActivity.this, Permission.MANAGE_EXTERNAL_STORAGE)) {File file = new File(path);if (file.exists()) {file.delete();}file.getParentFile().mkdirs();
​// 创建DocumentPdfWriter writer = null;try {writer = new PdfWriter(new FileOutputStream(path));} catch (FileNotFoundException e) {Log.e("FileNotFoundException", e.toString());}
​PdfDocument pdf_document = new PdfDocument(writer);// 生成的PDF文档信息PdfDocumentInfo info = pdf_document.getDocumentInfo();// 标题info.setTitle("First pdf file");// 作者info.setAuthor("Quinto");// 科目info.setSubject("test");// 关键词info.setKeywords("pdf");// 创建日期info.setCreator("2022-10-20");
​Document document = new Document(pdf_document, PageSize.A4, false);
​// 文字字体(显示中文)、大小、颜色PdfFont font = null;try {font = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");} catch (IOException e) {Log.e("IOException", e.toString());}float title_size = 36.0f;float text_title_size = 30.0f;float text_size = 24.0f;Color title_color = new DeviceRgb(0, 0, 0);Color text_title_color = new DeviceRgb(65, 136, 160);Color text_color = new DeviceRgb(43, 43, 43);
​// 行分隔符// 实线:SolidLine()  点线:DottedLine()  仪表盘线:DashedLine()LineSeparator separator = new LineSeparator(new SolidLine());separator.setStrokeColor(new DeviceRgb(0, 0, 68));
​// 添加大标题Text title = new Text("这里是pdf文件标题").setFont(font).setFontSize(title_size).setFontColor(title_color);Paragraph paragraph_title = new Paragraph(title).setTextAlignment(TextAlignment.CENTER);document.add(paragraph_title);
​for (int i = 1; i < 10; i++) {// 添加文本小标题Text text_title = new Text("第" + i + "行:").setFont(font).setFontSize(text_title_size).setFontColor(text_title_color);Paragraph paragraph_text_title = new Paragraph(text_title);document.add(paragraph_text_title);
​// 添加文本内容String content = "我是文本内容" + i + i + i + i + i + i + i + i + i + i;Text text = new Text(content).setFont(font).setFontSize(text_size).setFontColor(text_color);Paragraph paragraph_text = new Paragraph(text);document.add(paragraph_text);
​// 添加可换行空间document.add(new Paragraph(""));// 添加水平线document.add(separator);// 添加可换行空间document.add(new Paragraph(""));}
​/*** 添加列表*/List list = new List().setSymbolIndent(12).setListSymbol("\u2022").setFont(font);list.add(new ListItem("列表1")).add(new ListItem("列表2")).add(new ListItem("列表3"));document.add(list);
​/*** 添加图片*/Text text_image = new Text("图片:").setFont(font).setFontSize(text_title_size).setFontColor(text_title_color);Paragraph image = new Paragraph(text_image);document.add(image);Image image1 = null;Image image2 = null;Image image3 = null;try {/*image1 = new Image(ImageDataFactory.create("/storage/emulated/0/DCIM/Camera/IMG_20221003_181926.jpg")).setWidth(PageSize.A4.getWidth() * 2 / 3);image2 = new Image(ImageDataFactory.create("/storage/emulated/0/Download/互传/folder/证件/XHS_159716343059020494b83-da6a-39d7-ae3b-13fd92cfbb53.jpg")).setWidth(PageSize.A4.getWidth() / 3);image3 = new Image(ImageDataFactory.create("/storage/emulated/0/Download/互传/folder/证件/XHS_1597163520524f0b4df77-8db1-35c6-9dfa-3e0aa74f1fef.jpg")).setWidth(PageSize.A4.getWidth() / 3);*/
​image1 = new Image(ImageDataFactory.create("/storage/emulated/0/Pictures/JPEG_20230609_154240_8941441911022988116.jpg")).setWidth(PageSize.A4.getWidth() * 2 / 3);image2 = new Image(ImageDataFactory.create("/storage/emulated/0/Tencent/QQ_Images/-318f738d395e5630.jpg")).setWidth(PageSize.A4.getWidth() / 3);image3 = new Image(ImageDataFactory.create("/storage/emulated/0/Pictures/Screenshots/Screenshot_20230615_145522_com.hermes.wl.jpg")).setWidth(PageSize.A4.getWidth() / 3);
​} catch (MalformedURLException e) {Log.e("MalformedURLException", e.toString());}Paragraph paragraph_image = new Paragraph().add(image1).add("  ").add(image2).add("  ").add(image3);document.add(paragraph_image);document.add(new Paragraph(""));
​/*** 添加表格*/Text text_table = new Text("表单:").setFont(font).setFontSize(text_title_size).setFontColor(text_title_color);Paragraph paragraph_table = new Paragraph(text_table);document.add(paragraph_table);// 3列float[] pointColumnWidths = {100f, 100f, 100f};Table table = new Table(pointColumnWidths);// 设置边框样式、颜色、宽度Color table_color = new DeviceRgb(80, 136, 255);Border border = new DottedBorder(table_color, 3);table.setBorder(border);// 设置单元格文本居中table.setTextAlignment(TextAlignment.CENTER);// 添加单元格内容Color table_header = new DeviceRgb(0, 0, 255);Color table_content = new DeviceRgb(255, 0, 0);Color table_footer = new DeviceRgb(0, 255, 0);Text text1 = new Text("姓名").setFont(font).setFontSize(20.0f).setFontColor(table_header);Text text2 = new Text("年龄").setFont(font).setFontSize(20.0f).setFontColor(table_header);Text text3 = new Text("性别").setFont(font).setFontSize(20.0f).setFontColor(table_header);table.addHeaderCell(new Paragraph(text1));table.addHeaderCell(new Paragraph(text2));table.addHeaderCell(new Paragraph(text3));Text text4 = new Text("张三").setFont(font).setFontSize(15.0f).setFontColor(table_content);Text text5 = new Text("30").setFont(font).setFontSize(15.0f).setFontColor(table_content);Text text6 = new Text("男").setFont(font).setFontSize(15.0f).setFontColor(table_content);table.addCell(new Paragraph(text4));table.addCell(new Paragraph(text5));table.addCell(new Paragraph(text6));Text text7 = new Text("丽萨").setFont(font).setFontSize(15.0f).setFontColor(table_footer);Text text8 = new Text("20").setFont(font).setFontSize(15.0f).setFontColor(table_footer);Text text9 = new Text("女").setFont(font).setFontSize(15.0f).setFontColor(table_footer);table.addFooterCell(new Paragraph(text7));table.addFooterCell(new Paragraph(text8));table.addFooterCell(new Paragraph(text9));// 将表格添加进pdf文件document.add(table);
​/*** 添加页眉、页脚、水印*/Rectangle pageSize;PdfCanvas canvas;int n = pdf_document.getNumberOfPages();Log.i("zxd", "createPDF: " + n);for (int i = 1; i <= n; i++) {PdfPage page = pdf_document.getPage(i);Log.i("zxd", "createPDF page: " + page.getPageSize());pageSize = page.getPageSize();canvas = new PdfCanvas(page);// 页眉canvas.beginText().setFontAndSize(font, 7).moveText(pageSize.getWidth() / 2 - 18, pageSize.getHeight() - 10).showText("我是页眉").endText();
​// 页脚canvas.setStrokeColor(text_color).setLineWidth(.2f).moveTo(pageSize.getWidth() / 2 - 30, 20).lineTo(pageSize.getWidth() / 2 + 30, 20).stroke();canvas.beginText().setFontAndSize(font, 7).moveText(pageSize.getWidth() / 2 - 6, 10).showText(String.valueOf(i)).endText();
​// 水印Paragraph p = new Paragraph("Quinto").setFontSize(60);canvas.saveState();PdfExtGState gs1 = new PdfExtGState().setFillOpacity(0.2f);canvas.setExtGState(gs1);document.showTextAligned(p, pageSize.getWidth() / 2, pageSize.getHeight() / 2,pdf_document.getPageNumber(page),TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45);canvas.restoreState();}
​// 关闭document.close();Toast.makeText(this, "PDF文件已生成", Toast.LENGTH_SHORT).show();} else {//没权限//requestPermission();}
}

注意事项

1.pdfdocument.getPageSize()未设置为对象iText7的实例,获取页面大小的时候竟然报错,原因是立即刷新的参数设置成true了

可以告诉文档在默认情况下不要刷新其内容,方法是将false传递给构造函数中的第三个参数(immediateflush)

Document document = new Document(pdf_document, PageSize.A4, false);

pdfdocument.getPageSize()未设置为对象iText7的实例

2.如果图片资源没找到,会报io错误。最好在使用前,先判断下图片是否存在!

3.参考:Android使用iText7生成PDF文件

2.2 7.x的基础使用

2.2.1 显示中文

itext7在使用默认字体显示中文的时候,由于默认字库不支持中文,生成的pdf中的中文会显示空白,要解决这个问题需要自己引入字体。

1.下载一个中文的字体(.ttf文件,SourceHanSansCN.ttf)

2.加载本地字体样式

//InputStream inputStream = new FileInputStream(new File("SourceHanSansCN.ttf"));
//第三个参数为embedded,是否为内置字体,这里是自己提供的所以传false
//PdfFont font = PdfFontFactory.createFont(IOUtils.toByteArray(inputStream), PdfEncodings.IDENTITY_H, false);//Android 一般都是从资产文件获取字体,然后使用字体的byte[]创建禹卫硬笔字体
try {InputStream open = getAssets().open("禹卫硬笔.ttf");byte[] datas = new byte[open.available()];open.read(datas);font = PdfFontFactory.createFont(datas, PdfEncodings.IDENTITY_H, false);
} catch (IOException e) {Log.e("IOException", e.toString());
}

3.设置字体

//默认是A4纸大小
Document document = new Document(pdfDocument, new PageSize());
document.setFont(font);

2.2.2 分辨率

如果有pdf打印的需求,涉及到分辨率的问题。

在itext中除了插入的图片外其他都是矢量图所以不必担心分辨率的问题,只需要保证插入图片的分辨率即可,在itext中生成图片的分辨率默认是72dpi,这里是需要做一些处理的,如下:

int defaultDpi = 72;
int targetDpi = 300;
Image test = new Image(ImageDataFactory.create("test.jpg"));
test.scale(defaultDpi/targetDpi, defaultDpi/targetDpi);

2.2.3 布局

Itext坐标系如下图,当使用绝对布局的时候需要计算元素距离左侧和下侧的长度,当使用相对布局的时候,元素则是自上往下排列。

坐标原点图

绝对布局
setFixedPosition(float left, float bottom, float width)
相对布局

使用相对布局时,不需要setFixedPosition,只需要设置距离上下左右的长度即可。

setMarginLeft(float value);
setMarginRight(float value);
setMarginBottom(float value);
setMarginTop(float value);
setMargin(float commonMargin);

2.2.4 自动分页+生成页码

在使用绝对布局的时候,大部分情况下页面内容是固定大小的,位置也是固定的,分页也需要自己写代码来进行控制。但是如果pdf的内容不是固定的,这时候如果使用绝对布局就不是那么的灵活了,还需要自己计算元素的高度也设置绝对位置,这个时候我们选择使用相对布局则会更合适、简单一些。代码如下:
​
class PageEventHandler implements IEventHandler {private Document document;private PdfFont font;
​//由于需要统一和汉字的字体,所以加了一个pdffont参数,没有此需求的可以不加public PageEventHandler(Document document, PdfFont font){this.document = document;this.font = font;}
​@Overridepublic void handleEvent(Event event) {PdfDocumentEvent pdfEvent = (PdfDocumentEvent) event;PdfPage page = pdfEvent.getPage();PdfDocument pdfDoc = pdfEvent.getDocument();
​//获取当前页码int pageNumber = pdfDoc.getPageNumber(page);PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
​//在距离上个元素50的距离处写页码this.document.setTopMargin(50f);
​pdfCanvas.beginText().setFontAndSize(this.font, 32)//在页面一般宽的地方写上当前页面,由于字体定位的原点也在左下角,所以这里x轴减掉了页码宽度的一半,确保页码是写在中间的位置//y轴是距离底部20px.moveText(PageSize.A4.getWidth()/2-32/2, 20f).showText(String.valueOf(pageNumber)).endText();pdfCanvas.release();
​}
}
​
//使用
pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, new PageEventHandler(document, font));

2.2.5 常用组件

table
//不指定table每一列的宽度,根据列的内容自适应
public Table(int numColumns);
//指定每一列的宽度
public Table(float[] pointColumnWidths);
//向table里面添加单元格,默认是横着加,达到指定列数后,自动换行
public Table addCell(Cell cell);
//设置table或cell的边框,这里需要注意,如果不想显示边框,需要在每一个cell中都设置border为none,只设置table的边框还是会显示边框的
public T setBorder(Border border);
//无边框
cell.setBorder(Border.NO_BORDER);
//实线边框
cell.setBorder(new SolidBorder(1));
//虚线边框
cell.setBorder(new DashedBorder(1));
//四个都是圆角
cell.setBorderRadius(new BorderRadius(10f));
//圆角-右下角
cell.setBorderBottomRightRadius(new BorderRadius(10f));
//圆角-左下角
cell.setBorderBottomLeftRadius(new BorderRadius(10f));
//圆角-右上角
cell.setBorderTopRightRadius(new BorderRadius(10f));
//圆角-左上角
cell.setBorderTopLeftRadius(new BorderRadius(10f));
//关于圆角这一部分,table中的圆角,似乎设置了并不能生效,不过可以通过将外围的table的border设置为none,内部的cell设置边框实现
用table实现一个进度条
  • 思路:外层一个一行一列的table,内部cell再套一个一行一列的table作为实际的进度,根据百分比计算内部table的宽度

//percent这里传的是小数
public void processBar(Document document, float width, float percent){float processWidth = width * percent;DeviceRgb processColor = new DeviceRgb(11,11,11);//随便写个颜色吧DeviceRgb processBgColor = new DeviceRgb(101,11,11);//随便写个颜色吧Table table = new Table(new float[]{width}).setMargin(0).setPadding(0);//把不必要的空隙都扼杀在摇篮里Table processTable = new Table(new float[]{processWidth}).setMargin(0).setPadding(0);processTable.addCell(new Cell().setBorder(Border.NO_BORDER).setMargin(0).setPadding(0).setBackgroundColor(processColor));table.addCell(new Cell().setBorder(Border.NO_BORDER).setBackgroundColor(processBgColor).setMargin(0).setPadding(0).setBorder(Border.NO_BORDER).add(processTable));
​
}
Paragraph

paragraph应该是最经常使用的一个类了,pdf中写文字都会用到这个类。

Paragraph para = new Paragraph("我是一个paragraph").setFontSize(14)//设置字体大小.setBold()//设置文字为粗体.setFontColor(new DeviceRgb(0,0,0))//设置字体颜色.setTextAlignment(TextAlignment.CENTER)//文字水平居中.setFixedLeading(14);//类似于css中的行高

有的时候会有一些需求是,同一段落的一段文字中有部分文字需要设置成别的颜色或样式样式,这时候可以Text来处理,如下:

//这种处理方式不能处理加粗的问题,如果想要加粗的文字正好在段落的中间,但是如果设置为粗体会导致整段文字都变成粗体(斜体是同理的)
para.add(new Text("我想与众不同").setFontSize("20").setFontColor(new DeviceRgb(255,255,255)));

Image
//图片分辨率问题见上面分辨率部分
Image image = new Image(ImageDataFactory.create("/home/test.jpg"));

参考:Itext7生成pdf最全api总结

2.2.6 基本使用

private void pdf1() {path = getExternalFilesDir("").getAbsolutePath() + "/itext_basic.pdf";PdfWriter writer;//创建一个写入传递的输出流的 PdfWriter。try {writer = new PdfWriter(new FileOutputStream(path));PdfFont font = PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN);PdfDocument pdf = new PdfDocument(writer);Document document = new Document(pdf);Text text = new Text("Hello World PDF created using iText").setFont(font).setFontSize(15).setFontColor(ColorConstants.MAGENTA);//Add paragraph to the documentdocument.add(new Paragraph(text));document.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}
}

2.2.7 字体样式

try {PdfDocument pdf = new PdfDocument(new PdfWriter(path));PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER);Style style = new Style().setFont(font).setFontSize(14).setFontColor(ColorConstants.RED).setBackgroundColor(ColorConstants.YELLOW);
​Document document = new Document(pdf);document.add(new Paragraph().add("In this PDF, ").add(new Text("Text is styled").addStyle(style)).add(" using iText ").add(new Text("Style").addStyle(style)).add("."));document.close();
​
} catch (IOException e) {e.printStackTrace();
}

2.2.8 txt转pdf

private void pdf3(String source, String des) {try {BufferedReader br = new BufferedReader(new FileReader(source));PdfDocument pdf = new PdfDocument(new PdfWriter(des));Document document = new Document(pdf);String line;PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER);while ((line = br.readLine()) != null) {document.add(new Paragraph(line).setFont(font));}br.close();document.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}
}

参考:使用 iText 7在 Java 中生成中文PDF

2.3 打开pdf

/*** 打开PDF文件*/
private void openPDF() {new Handler().postDelayed(new Runnable() {@Overridepublic void run() {try {FileUtils.openFile(context, new File(path));} catch (Exception e) {Log.e("Exception", e.toString());}}}, 1000);
}

打开PDF文件

/*** 打开PDF文件** @param context* @param url* @throws ActivityNotFoundException* @throws IOException*/
public static void openFile(Context context, File url) throws ActivityNotFoundException {if (url.exists()) {Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileprovider", url);
​String urlString = url.toString().toLowerCase();
​Intent intent = new Intent(Intent.ACTION_VIEW);intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);/*** Security*/List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);for (ResolveInfo resolveInfo : resInfoList) {String packageName = resolveInfo.activityInfo.packageName;context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);}
​// 通过比较url和扩展名,检查您要打开的文件类型。// 当if条件匹配时,插件设置正确的意图(mime)类型// 所以Android知道用什么程序打开文件if (urlString.toLowerCase().contains(".doc")|| urlString.toLowerCase().contains(".docx")) {// Word documentintent.setDataAndType(uri, "application/msword");} else if (urlString.toLowerCase().contains(".pdf")) {// PDF fileintent.setDataAndType(uri, "application/pdf");} else if (urlString.toLowerCase().contains(".ppt")|| urlString.toLowerCase().contains(".pptx")) {// Powerpoint fileintent.setDataAndType(uri, "application/vnd.ms-powerpoint");} else if (urlString.toLowerCase().contains(".xls")|| urlString.toLowerCase().contains(".xlsx")) {// Excel fileintent.setDataAndType(uri, "application/vnd.ms-excel");} else if (urlString.toLowerCase().contains(".zip")|| urlString.toLowerCase().contains(".rar")) {// ZIP fileintent.setDataAndType(uri, "application/trap");} else if (urlString.toLowerCase().contains(".rtf")) {// RTF fileintent.setDataAndType(uri, "application/rtf");} else if (urlString.toLowerCase().contains(".wav")|| urlString.toLowerCase().contains(".mp3")) {// WAV/MP3 audio fileintent.setDataAndType(uri, "audio/*");} else if (urlString.toLowerCase().contains(".gif")) {// GIF fileintent.setDataAndType(uri, "image/gif");} else if (urlString.toLowerCase().contains(".jpg")|| urlString.toLowerCase().contains(".jpeg")|| urlString.toLowerCase().contains(".png")) {// JPG fileintent.setDataAndType(uri, "image/jpeg");} else if (urlString.toLowerCase().contains(".txt")) {// Text fileintent.setDataAndType(uri, "text/plain");} else if (urlString.toLowerCase().contains(".3gp")|| urlString.toLowerCase().contains(".mpg")|| urlString.toLowerCase().contains(".mpeg")|| urlString.toLowerCase().contains(".mpe")|| urlString.toLowerCase().contains(".mp4")|| urlString.toLowerCase().contains(".avi")) {// Video filesintent.setDataAndType(uri, "video/*");} else {// 如果你愿意,你也可以为任何其他文件定义意图类型// 另外,使用下面的else子句来管理其他未知扩展// 在这种情况下,Android将显示设备上安装的所有应用程序// 因此您可以选择使用哪个应用程序intent.setDataAndType(uri, "*/*");}intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);} else {Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show();}
}

manifest.xml

<providerandroid:name="androidx.core.content.FileProvider"android:authorities="com.zg.pdfdemo.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths" />
</provider>

provider_paths

<?xml version="1.0" encoding="utf-8"?>
<paths><external-path name="external_files" path="."/>
</paths>

2.4 pdfRender的简单使用

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void pdf4() {path = getExternalFilesDir("").getAbsolutePath() + "/itext_basic.pdf";try {//  1.创建文件File pdfFile = new File(path);//  2.获取 ParcelFileDescriptor 对象ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY);//  3.创建PdfRenderer对象PdfRenderer renderer = new PdfRenderer(parcelFileDescriptor);
​//渲染page数据到bitmap//1、获取页码数据Page对象PdfRenderer.Page page = renderer.openPage(0);int pageCount = renderer.getPageCount();Log.i("zxd", "pdf4: 总数=" + pageCount);
​//2.创建ARGB_8888 的bitmapBitmap bitmap = Bitmap.createBitmap(page.getWidth(), page.getHeight(), Bitmap.Config.ARGB_8888);//3.渲染page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);//渲染到iv中binding.iv.setImageBitmap(bitmap);//关闭当前page数据page.close();
​} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}
}

总结

优点:本身是Android提供的,比较轻量无需依赖第三方SDK,核心代码都是native实现,执行效率比较高

缺点:只能在Android5.0 或以上版本使用,由于实现方式是native 所以无法自己定制渲染算法,以及方式、使用时还需要自己控制线程安全比较繁琐

参考:

[API Reference Document](https://www.apiref.com/)

Android PdfRenderer 简单使用

Android使用pdfRenderer实现PDF展示功能

itext5

相关文章:

Android 生成pdf文件

Android 生成pdf文件 1.使用官方的方式 使用官方的方式也就是PdfDocument类的使用 1.1 基本使用 /**** 将tv内容写入到pdf文件*/RequiresApi(api Build.VERSION_CODES.KITKAT)private void newPdf() {// 创建一个PDF文本对象PdfDocument document new PdfDocument();//创建…...

Kafka 入门到起飞 - 生产者发送消息流程解析

生产者通过send&#xff08;&#xff09;方法发送消息消息会经过拦截器->序列化器->分区器 进行加工然后将消息存在缓冲区当缓冲区中消息达到条件会按批次发送到broker对应分区上broker将接收到的消息进行刷盘持久化消息处理broker会返回给producer响应落盘成功返回元数据…...

基于单片机智能台灯坐姿矫正器视力保护器的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;LCD1602液晶显示当前当前光线强度、台灯灯光强度、当前时间、坐姿距离等&#xff1b;按键设置当前时间&#xff0c;闹钟、提醒时间、坐姿最小距离&#xff1b;通过超声波检测坐姿&#xff0c;当坐姿不正容易对眼睛和身体腰部等造成…...

欧姆龙以太网模块如何设置ip连接 Kepware opc步骤

在数字化和自动化的今天&#xff0c;PLC在工业控制领域的作用日益重要。然而&#xff0c;PLC通讯口的有限资源成为了困扰工程师们的问题。为了解决这一问题&#xff0c;捷米特推出了JM-ETH-CP转以太网模块&#xff0c;让即插即用的以太网通讯成为可能&#xff0c;不仅有效利用了…...

PLEX如何搭建个人局域网的视频网站

Plex是一款功能非常强大的影音媒体管理系统&#xff0c;最大的优势是多平台支持和界面优美&#xff0c;几乎可以在所有的平台上安装plex服务器和客户端&#xff0c;让你可以随时随地享受存储在家中的电影、照片、音乐&#xff0c;并且可以实现观看记录无缝衔接&#xff0c;手机…...

java学习02

一、基本数据类型 Java有两大数据类型&#xff0c;内置数据类型和引用数据类型。 内置数据类型 Java语言提供了八种基本类型。六种数字类型&#xff08;四个整数型&#xff0c;两个浮点型&#xff09;&#xff0c;一种字符类型&#xff0c;还有一种布尔型。 byte&#xff1…...

libcurl库使用实例

libcurl libcurl是一个功能强大的跨平台网络传输库&#xff0c;支持多种协议&#xff0c;包括HTTP、FTP、SMTP等&#xff0c;同时提供了易于使用的API。 安装 ubuntu18.04平台安装 sudo apt-get install libcurl4-openssl-dev实例 这个示例使用libcurl库发送一个简单的HTTP …...

大数据存储架构详解:数据仓库、数据集市、数据湖、数据网格、湖仓一体

前言 本文隶属于专栏《大数据理论体系》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见大数据理论体系 思维导图 数据仓库 数据仓库是一个面向主题的&…...

ESP32(MicroPython) 网页控制五自由度机械臂

ESP32&#xff08;MicroPython&#xff09; 网页控制五自由度机械臂 本程序通过网页控制五自由度机械臂&#xff0c;驱动方案改用PCA9685。 代码如下 #导入Pin模块 from machine import Pin import time from machine import SoftI2C from servo import Servos import networ…...

前端笔记_OAuth规则机制下实现个人站点接入qq三方登录

文章目录 ⭐前言⭐qq三方登录流程&#x1f496;qq互联中心创建网页应用&#x1f496;配置回调地址redirect_uri&#x1f496;流程分析 ⭐思路分解⭐技术选型实现&#x1f496;技术选型&#xff1a;&#x1f496;实现 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本…...

huggingface新作品:快速和简便的训练模型

AutoTrain Advanced是一个用于训练和部署最先进的机器学习模型的工具。它旨在提供更快速、更简便的方式来进行模型训练和部署。 安装 您可以通过PIP安装AutoTrain-Advanced的Python包。请注意&#xff0c;为了使AutoTrain Advanced正常工作&#xff0c;您将需要python > 3.…...

利用鸿鹄优化共享储能的SCADA 系统功能,赋能用户数据自助分析

摘要 本文主要介绍了共享储能的 SCADA 系统大数据架构&#xff0c;以及如何利用鸿鹄来更好的优化 SCADA 系统功能&#xff0c;如何为用户进行数据自助分析赋能。 1、共享储能介绍 说到共享储能&#xff0c;可能不少朋友比较陌生&#xff0c;下面我们简单介绍一下共享储能的价值…...

noSQL语句练习

Redis练习题 string list hash结构中&#xff0c;每个至少完成5个命令&#xff0c;包含插入 修改 删除 查询&#xff0c;list 和hash还需要增加遍历的操作命令 1、 string类型数据的命令操作&#xff1a; &#xff08;1&#xff09; 设置键值&#xff1a; 127.0.0.1:63…...

Spring:Bean生命周期

Bean 生命周期生命周期 Bean 生命周期是 bean 对象从创建到销毁的整个过程。 简单的 Bean 生命周期的过程: 1.实例化(调用构造方法对 bean 进行实例化) 2.依赖注入(调用 set 方法对 bean 进行赋值) 3.初始化(手动配置 xml 文件中 bean 标签的 init-method 属性值,来指…...

Vue自定义指令

需求1&#xff1a;定义一个v-big指令&#xff0c;和v-text功能类似&#xff0c;但会把绑定的数值放大10倍。 需求2&#xff1a;定义一个v-fbind指令&#xff0c;和v-bind功能类似&#xff0c;但可以让其所绑定的input元素默认获取焦点。 自定义指令函数式v-big&#xff1a; &l…...

SpringBoot+JWT实现单点登录解决方案

一、什么是单点登录? 单点登录是一种统一认证和授权机制&#xff0c;指在多个应用系统中&#xff0c;用户只需要登录一次就可以访问所有相互信任的系统&#xff0c;不需要重新登录验证。 单点登录一般用于互相授信的系统&#xff0c;实现单一位置登录&#xff0c;其他信任的…...

中国国债发行数据集(2002-2023)

国债是由国家发行的债券&#xff0c;由于国债的发行主体是国家&#xff0c;所以它具有最高的信用度&#xff0c;被公认为是最安全的投资工具。国债按照交易市场的不同分为三类&#xff0c;即银行间市场国债、交易所市场国债和柜台市场国债&#xff1b;按照交易方式的不同分为三…...

曲师大2023大一新生排位赛-C.String题解

C.String 题目描述 众所周知&#xff0c;许师哥精通字符串。 一天&#xff0c;许师哥意外的获得了一个字符串&#xff0c;但他发现这个字符串并不是一个回文串&#xff0c;因此他非常生气。于是他决定从这个字符串中删除若干个字符使得 剩余的字符串为一个回文串。 回想回文串…...

Linux Ubuntu安装RabbitMQ服务

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…...

什么是测试用例?如何设计?

在学习或者实际的测试工作中经常都会提到“测试用例”这个词&#xff0c;没错&#xff0c;测试用例是测试工作的核心&#xff0c;不管要做的是什么样的测试&#xff0c;在真正动手执行测试之前&#xff0c;我们都需要先根据软件需求来设计测试用例&#xff0c;之后再依据设计好…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...