jspdf:即时生成pdf的插件,支持表单图文混排

介绍

jspdf是即时生成pdj的插件,支持通过html代码或源数据生成。

快速开始

安装

官方给出的安装方式有三种:

个人觉得通过npm安装比较省事。

demo

1、这个链接🔗展示了插件可实现的效果:除了基础的文字表格混排外,还展示了合并单元格、自定义表格风格、页眉页脚等效果。

2、这个链接🔗提供了nodejs、ts和webpack的示例代码,另外在这个链接🔗可以查看html的示例代码。


核心代码

调用插件的核心代码如下:

const doc = new jsPDF()
autoTable(doc, { ... }) //花括号内为pdf内容和样式的代码,可以是html或json格式
doc.save('table.pdf') //保存文件。注意文件路径应包含文件名

等价于以下写法:

const doc = new jsPDF()
doc.autoTable({ ... }) 
doc.save('table.pdf')

后端写法

以下为文档给出的usage,其它框架的示例可以在这个链接🔗查看。

需要注意的是,不论你是使用上一节所述的哪一种写法,jsPDF和autoTable这两个插件都必须引入

import jsPDF from 'jspdf'
import autoTable from 'jspdf-autotable'

const doc = new jsPDF()

// It can parse html:
// <table id="my-table"><!-- ... --></table>
autoTable(doc, { html: '#my-table' })

// Or use javascript directly:
autoTable(doc, {
  head: [['Name', 'Email', 'Country']],
  body: [
    ['David', 'david@example.com', 'Sweden'],
    ['Castille', 'castille@example.com', 'Spain'],
    // ...
  ],
})

// Sometimes you might have to call the default function on the export (for example in Deno)
autoTable.default(doc, { html: '#my-table' })

doc.save('table.pdf')

HTML写法

以下同样为官方文档中的示例,需要通过本地文件或CDN的方式引入插件。

<script src="jspdf.min.js"></script>
<script src="jspdf.plugin.autotable.min.js"></script>
<script>
  var doc = new jsPDF()
  doc.autoTable({ html: '#my-table' })
  doc.save('table.pdf')
</script>

Let we go

由于没有试过用html配置pdf内容,故按下不表。本节介绍通过源数据进行设置的常用操作。

配置

插件支持对内容、样式、表单进行配置,具体写法请见文档

常用操作

文本

1、设置文本字体和字号:

引用的字体ttf文件得放在app/public/font路径下

doc.setFont('FZDBSJW'); //方正大标宋简体
doc.setFontSize(10.5); //五号

2、绘制文字

doc.text(lrSpace, y, data); 
  • lrSpace:左右外边距
  • y:上下外边距
  • data:文本数据

图像

1、绘制图像

doc.addImage(img_obj, format, x, y, img_width, img_height);
  • img_obj:node-base64-image模块返回的对象
const base64 = require('node-base64-image');
const img_obj = await base64.encode(img_url);
  • format:图片的格式,字符串类型。如"png"、"jpg"
  • x、y:图像左上角的坐标
  • img_width、img_height:图像的宽和高

表格

可以配置的option详见文档。以下为本人手写的示例代码,供学习参考:

doc.autoTable({
    styles: { // 设置表格的字体,不然表格中文也乱码
        fillColor: [ 255, 255, 255 ],
        font: 'FZDBSJW',
        textColor: [ 0, 0, 0 ],
        halign: 'left',
        fontSize: 10.5, // 五号
    },
    columnStyles: {
        0: {
            cellWidth: img_height*2 + 2, //旧版本中为columnWidth
            minCellHeight: img_height * 0.6
        },
        1: {
            cellWidth: img_height*2 + 2, //如各列的cellWidth不一致,将取最大值
            minCellHeight: img_height * 0.6
        },
    },
    headStyles: { // 设置表头样式
        fillColor: '#fff', // 背景颜色
        textColor: '#000', // 文字颜色
        lineColor: '#D3D3D3', // 线颜色
        lineWidth: 0.1, // 线宽
    },
    theme: 'grid', // 主题
    body: [{}], // 表格内容
    columns: [ // 表头
    {
        header: header1,
        dataKey: 'header1',
    },
    {
        header: header2,
        dataKey: 'header2',
    },
    ],
    startY: y, // 距离上边的距离
    margin: lrSpace, // 距离左右边的距离
    didDrawCell: (data) => {
        if (data.column.index === 0) {
            cellY = data.cell.y; //获取左上角位置
            cellHeight = data.cell.height; //获取格子高度
        }
    },
});

1、body支持列表和key-value两种写法:

 body:[
     ['David', 'david@example.com'],  
     ['Castille', 'castille@example.com']
 ]

等价于:

 body:[
     {'header1':'David', 'header2':'david@example.com'},  
     {'header1':'Castille', 'header2':'castille@example.com'}
 ]

2、使用hooks可以实现精细的自定义,详见文档

  • didParseCell:(HookData)=>{}-当插件完成对单元格内容的解析时调用。可用于替代特定单元格的内容或样式。
  • willDrawCell:(HookData)=>{}-在绘制单元格或行之前调用。可用于调用本地jspdf样式函数,如doc.setTextColor或在绘制文本之前更改文本的位置等。
  • didDrawCell:(HookData)=>{}-在将单元格添加到页面后调用。可用于绘制其他单元格内容,如带有doc.addImage的图像、带有doc.add text的其他文本或其他jspdf形状。
  • willDrawPage:(HookData)=>{}-在开始绘图之前调用。可以用来添加页眉或任何其他内容,您希望在每个页面上都有一个自动表格。
  • didDrawPage:(HookData)=>{}-在插件完成在页面上绘制所有内容后调用。可用于添加带有页码的页脚或您希望在每页上添加的任何其他内容—有一个自动表格。

通过didDrawCell方法,我们可以在表格中插入图片,效果如下:

代码接上面的例子:

var idx = 1;
doc.autoTable({
   // ... 省去前38行代码
   didDrawCell: (data) => {
        if (data.column.index === 0) {
            cellY = data.cell.y; //获取左上角位置
            cellHeight = data.cell.height; //获取格子高度
        }
        if (idx === 3 && data.column.index === 0) {
            doc.addImage(img_obj, 'png', data.cell.x+1, data.cell.y + 1, img_width, img_height);
        }
        if (idx === 4 && data.column.index === 1) {        
            doc.addImage(img_obj, 'png', data.cell.x+1, data.cell.y + 1, img_width, img_height);
            doc.addImage(img_obj, "png", data.cell.x + img_height + 1, data.cell.y + 1, img_height, img_height/2); 
        }
        idx++;
    },
});

由于data.row.index并不能表示所在行号,因此另外定义了一个自增变量。

data.column包含字段:

{
  "wrappedWidth": 82,
  "minReadableWidth": 51.68194444444444,
  "minWidth": 82,
  "width": 82,
  "dataKey": "header1",
  "raw": {
    "header": "header1",
    "dataKey": "header1"
  },
  "index": 1
}

data.row包含的字段:

{
  "height": 42,
  "raw": {},
  "index": 0,
  "section": "body",
  "cells": {
    "0": {
      "contentHeight": 42,
      "contentWidth": 3.5277777777777772,
      "wrappedWidth": 82,
      "minReadableWidth": 3.5277777777777772,
      "minWidth": 82,
      "width": 82,
      "height": 42,
      "x": 25,
      "y": 97.9325,
      "styles": {
        "font": "FZDBSJW",
        "fontStyle": "normal",
        "overflow": "linebreak",
        "fillColor": "#fff",
        "textColor": "#000",
        "halign": "left",
        "valign": "top",
        "fontSize": 10.5,
        "cellPadding": 1.7638888888888886,
        "lineColor": "#D3D3D3",
        "lineWidth": 0.1,
        "cellWidth": 82,
        "minCellHeight": 42,
        "minCellWidth": 0
      },
      "section": "body",
      "rowSpan": 1,
      "colSpan": 1,
      "text": [
        ""
      ]
    },
    "1": {
      "contentHeight": 42,
      "contentWidth": 3.5277777777777772,
      "wrappedWidth": 82,
      "minReadableWidth": 3.5277777777777772,
      "minWidth": 82,
      "width": 82,
      "height": 42,
      "x": 107,
      "y": 97.9325,
      "styles": {
        "font": "FZDBSJW",
        "fontStyle": "normal",
        "overflow": "linebreak",
        "fillColor": "#fff",
        "textColor": "#000",
        "halign": "left",
        "valign": "top",
        "fontSize": 10.5,
        "cellPadding": 1.7638888888888886,
        "lineColor": "#D3D3D3",
        "lineWidth": 0.1,
        "cellWidth": 82,
        "minCellHeight": 42,
        "minCellWidth": 0
      },
      "section": "body",
      "rowSpan": 1,
      "colSpan": 1,
      "text": [
        ""
      ]
    },
    "img_obj": {
      "contentHeight": 42,
      "contentWidth": 3.5277777777777772,
      "wrappedWidth": 82,
      "minReadableWidth": 3.5277777777777772,
      "minWidth": 82,
      "width": 82,
      "height": 42,
      "x": 25,
      "y": 97.9325,
      "styles": {
        "font": "FZDBSJW",
        "fontStyle": "normal",
        "overflow": "linebreak",
        "fillColor": "#fff",
        "textColor": "#000",
        "halign": "left",
        "valign": "top",
        "fontSize": 10.5,
        "cellPadding": 1.7638888888888886,
        "lineColor": "#D3D3D3",
        "lineWidth": 0.1,
        "cellWidth": 82,
        "minCellHeight": 42,
        "minCellWidth": 0
      },
      "section": "body",
      "rowSpan": 1,
      "colSpan": 1,
      "text": [
        ""
      ]
    }   
  },
  "spansMultiplePages": false
}

实现图文混排,可以使用一个自定义的 EditText 控件,并在其中添加一个 ImageView 控件来实现。 以下是实现方法: 1.创建一个自定义的带有 ImageView 控件的 EditText 控件。 ```java public class RichEditText extends androidx.appcompat.widget.AppCompatEditText { public RichEditText(Context context) { super(context); } public RichEditText(Context context, AttributeSet attrs) { super(context, attrs); } public RichEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } //插入图片 public void insertImage(Bitmap bitmap) { SpannableString ss = new SpannableString(" "); ImageSpan span = new ImageSpan(getContext(), bitmap); ss.setSpan(span, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); append(ss); } } ``` 2.在布局文件中使用自定义的 EditText 控件。 ```xml <com.example.richedittextdemo.RichEditText android:id="@+id/edit_text" android:layout_width="match_parent" android:layout_height="wrap_content" /> ``` 3.在 Activity 中获取自定义的 EditText 控件,并添加一个点击事件。 ```java public class MainActivity extends AppCompatActivity { private RichEditText editText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = findViewById(R.id.edit_text); editText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //调用选择图片的方法 chooseImage(); } }); } //选择图片 private void chooseImage() { Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(intent, 1); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK && requestCode == 1) { try { Uri selectedImage = data.getData(); InputStream inputStream = getContentResolver().openInputStream(selectedImage); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); editText.insertImage(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } } } ``` 4.在 onActivityResult 方法中获取选择的图片,并调用自定义的 EditText 控件的 insertImage 方法,将图片插入到 EditText 中。 以上就是在 Android 中实现 EditText 插入图片并实现图文混排的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值