Android 生成pdf文件

news2024/11/25 9:58:24

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>
​
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".pdf.PdfActivity">
​
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
​
            <TextView
                android:id="@+id/pdf_tv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="" />
​
            <ImageView
                android:id="@+id/pdf_iv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:scaleType="fitXY"
                android:src="@mipmap/logo"
                android:visibility="visible" />
​
            <Button
                android: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

/**
 * 创建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();
​
        // 创建Document
        PdfWriter 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错误

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

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;
    }
​
    @Override
    public 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 document
        document.add(new Paragraph(text));
        document.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.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 block
        e.printStackTrace();
    }
}

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

2.3 打开pdf

/**
 * 打开PDF文件
 */
private void openPDF() {
    new Handler().postDelayed(new Runnable() {
        @Override
        public 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 document
            intent.setDataAndType(uri, "application/msword");
        } else if (urlString.toLowerCase().contains(".pdf")) {
            // PDF file
            intent.setDataAndType(uri, "application/pdf");
        } else if (urlString.toLowerCase().contains(".ppt")
                || urlString.toLowerCase().contains(".pptx")) {
            // Powerpoint file
            intent.setDataAndType(uri, "application/vnd.ms-powerpoint");
        } else if (urlString.toLowerCase().contains(".xls")
                || urlString.toLowerCase().contains(".xlsx")) {
            // Excel file
            intent.setDataAndType(uri, "application/vnd.ms-excel");
        } else if (urlString.toLowerCase().contains(".zip")
                || urlString.toLowerCase().contains(".rar")) {
            // ZIP file
            intent.setDataAndType(uri, "application/trap");
        } else if (urlString.toLowerCase().contains(".rtf")) {
            // RTF file
            intent.setDataAndType(uri, "application/rtf");
        } else if (urlString.toLowerCase().contains(".wav")
                || urlString.toLowerCase().contains(".mp3")) {
            // WAV/MP3 audio file
            intent.setDataAndType(uri, "audio/*");
        } else if (urlString.toLowerCase().contains(".gif")) {
            // GIF file
            intent.setDataAndType(uri, "image/gif");
        } else if (urlString.toLowerCase().contains(".jpg")
                || urlString.toLowerCase().contains(".jpeg")
                || urlString.toLowerCase().contains(".png")) {
            // JPG file
            intent.setDataAndType(uri, "image/jpeg");
        } else if (urlString.toLowerCase().contains(".txt")) {
            // Text file
            intent.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 files
            intent.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

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.zg.pdfdemo.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android: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 的bitmap
        Bitmap 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 block
        e.printStackTrace();
    }
}

总结

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

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

参考:

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

Android PdfRenderer 简单使用

Android使用pdfRenderer实现PDF展示功能

itext5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/750698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是从人类反馈中强化学习(RLHF)?

自从OpenAI公司发布ChatGPT以来&#xff0c;人们对大型语言模型(LLM)的这一重大进步感到兴奋。虽然ChatGPT与其他最先进的大型语言模型大小相同&#xff0c;但其性能要高得多&#xff0c;并且承诺支持新的应用程序或颠覆取代原有的应用程序。 ChatGPT的惊人表现背后的主要原因…

NAT—网络地址转换

目录 静态NAT 动态NAT NAPT—easy IP 多对多的NAPT 端口映射—高级用法 NAT—网络地址转换 IPV4地址不够用 NAT ABC—三类地址中截取了一部分地址&#xff08;并且让这部分地址可以重复使用&#xff09;—私网地址 A类地址中&#xff1a;10.0.0.0-10.255.255.255 &#xff08;…

3. 学习分类 - 基于图像大小进行分类

3.1 设置问题 根据图片的尺寸&#xff0c;把图片分为纵向图像和横向图像。这种把图像分成两种类别的问题&#xff0c;就是二分类问题。 纵向图片示例&#xff1a; 横向图片示例&#xff1a; 这样就有了两个训练数据&#xff1a; 增加训练数据&#xff0c;并在图像中表示出来…

mac批量修改文件名为不同名字

mac批量修改文件名为不同名字怎么弄&#xff1f;很多小伙伴通过私信向我求助&#xff0c;用什么方法可以在mac电脑上批量修改文件名称&#xff0c;将大量文件修改成不同的名称。这可能是一项比较麻烦的操作&#xff0c;在电脑上进行过批量重命名的小伙伴都知道&#xff0c;一般…

汉服小姐姐【InsCode Stable Diffusion美图活动一期】

一、 Stable Diffusion 模型在线使用地址&#xff1a;https://inscode.csdn.net/inscode/Stable-Diffusion 二、模型版本及相关配置&#xff1a; 模型&#xff1a;majicmixRealistic_v6 Lora&#xff1a;hanfu_ming 采样迭代步数&#xff08;steps&#xff09;: 40 采样方法&am…

SCB后备保护器——保护电器的后备力量

在现代社会中&#xff0c;电力设备已经成为了不可或缺的一部分&#xff0c;而在使用电力设备的过程中&#xff0c;由于各种原因&#xff0c;电力设备可能会受到电涌的影响&#xff0c;从而导致设备损坏或者火灾事故的发生。为了有效保护电力设备的安全稳定运行&#xff0c;研发…

数据科学分析全流程步骤

知识图谱以结构化的“知识”来存储与表示海量数据&#xff0c;作为承载底层海量知识并支持上层智能应用的重要载体&#xff0c;它在智能时代中扮演了极其重要的角色。然而&#xff0c;由于知识图谱高度结构化的特点&#xff0c;我们常常需要构建结构化查询语句&#xff08;SPAR…

卷积神经网络(CNN)原理详解

近些年人工智能发展迅速&#xff0c;在图像识别、语音识别、物体识别等各种场景上深度学习取得了巨大的成功&#xff0c;例如AlphaGo击败世界围棋冠军&#xff0c;iPhone X内置了人脸识别解锁功能等等&#xff0c;很多AI产品在世界上引起了很大的轰动。 而其中 卷积神经网络&am…

微服务 云原生:gRPC 客户端、服务端的通信原理

gRPC Hello World protoc 是 Protobuf 的核心工具&#xff0c;用于编写 .proto 文件并生成 protobuf 代码。在这里&#xff0c;以 Go 语言代码为例&#xff0c;进行 gRPC 相关代码编写。 下载 protoc 工具&#xff1a;https://github.com/protocolbuffers/protobuf/releases&a…

饭堂人群密度检测之Pythton

完整资料进入【数字空间】查看——baidu搜索"writebug" 一、饭堂人群密度检测 二、选题背景 在这个人工智能快速发展的时代&#xff0c;智能交通、智能机器人等人工智能化产品不断出现。作为人工智能的重要分支&#xff0c;计算机视觉起到了重要作用。它通过一系列的…

面试题更新之-使用 base64 编码的优缺点

文章目录 base64 编码是什么&#xff1f;使用 base64 编码的优缺点 base64 编码是什么&#xff1f; Base64编码是一种将二进制数据转换为ASCII字符的编码方式。它将三个字节的二进制数据分割成四组&#xff0c;每组6个比特&#xff0c;然后将这些6个比特转换为可打印的ASCII字…

前端学习——Web API (Day5)

BOM操作 Window对象 BOM 定时器-延时函数 案例 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&q…

XSS 攻击的检测和修复方法

XSS 攻击的检测和修复方法 XSS&#xff08;Cross-Site Scripting&#xff09;攻击是一种最为常见和危险的 Web 攻击&#xff0c;即攻击者通过在 Web 页面中注入恶意代码&#xff0c;使得用户在访问该页面时&#xff0c;恶意代码被执行&#xff0c;从而导致用户信息泄露、账户被…

Docker 部署 Jenkins (一)

Docker 部署 Jenkins (一) 一. 安装 jenkins $ mkdir -p /home/tester/data/docker/jenkins $ vim jenkins:lts-jdk11.sh./jenkins:lts-jdk11.sh 内容 #! /bin/bash mkdir -p /home/tester/data/docker/jenkins/jenkins_homesudo chown -R 1000:1000 /home/tester/data/dock…

解决Spring Data JPA查询存在缓存问题及解决方案

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

mysql5.7下载安装配置详细步骤(超详细)【软件下载+环境配置】

1 下载 官方下载地址&#xff1a;MySQL :: Download MySQL Installer 2 安装 双击下载的安装包 等待安装器加载 有些小伙伴在加载过程中可能会出现无法验证其身份或者提示你升级安装器 点击继续运行&#xff0c;不要升级 加载完成后出现这个界面 选择 custom——》next …

中国移动光猫设置桥接

网上教程五花八门&#xff0c;有些坑有些行&#xff0c;我试成功了&#xff0c;记录一下方法。 一、流程简述 1. 使用超级管理员账号登录中国移动光猫&#xff0c;设置桥接&#xff0c;并重启 2. 用网线连接路由器和光猫&#xff0c;登录路由器&#xff0c;设置宽带拨号&…

初识muysql之常见函数

目录 一、日期时间函数 1. 常见的日期时间函数 2. current_date() 3. current_time() 4. current_timestamp() 5. now() 6. date(datetime) 7. date_add(date, interval d_value_type) 8. date_sub(date, d_value_type) 9. datediff(date1, date2) 10. 题目示例 10…

从零开始 Spring Boot 69:JPA 条件查询

从零开始 Spring Boot 69&#xff1a;JPA 条件查询 图源&#xff1a;简书 (jianshu.com) 在之前的文章中我们学习过条件查询&#xff08;Criterial Query&#xff09;&#xff0c;构建条件查询的一般步骤是&#xff1a; 获取HibernateCriteriaBuilder利用HibernateCriteriaBu…

easy rule 学习记录

总体&#xff1a; 使用方面除了官网的wiki外&#xff0c;推荐阅读 作者&#xff1a;夜尽天明_ 链接&#xff1a;https://juejin.cn/post/7048917724126248967 来源&#xff1a;稀土掘金 非annotation 方式&#xff0c;执行不是jdk proxy模式annotation 方式&#xff0c;和ru…