于工作需要,要实现后端根据模板动态填充数据生成PDF文档,通过技术选型,使用Ireport5.6来设计模板,结合JasperReports5.6工具库来调用渲染生成PDF文档。
ireport的使用由于时间关系不便多说,设计好之后,将其进行编译生成jasper文件,然后将其拷贝放置于项目jasper目录下,以供访问获取该文件。
<!-- jasperreports-->
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-fonts</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-pdfa</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.11</version>
</dependency>
2、在src/main/resources路径下新建fonts目录,并新建fonts.xml文件
3、在src/main/resources/fonts目录放入宋体字体文件 simsun.ttf
4、线上(linux环境)预览效果如下:
如若转载,请注明出处:开源字节 https://sourcebyte.vip/article/335.html
计量领域中,计量检定是一种重要形式,主要用于评定计量器具的计量性能,确定其量值是否准确一致,实现手段包括计量检验、出具检定证书和加封盖印等。
在检定证书这一环节,存在一个难点,就是无法在线预览以及智能生成。
1、证书管理不能满足用户精准打印、特殊字符或多页打印的需求。因为在计量行业中,精密仪器较多,往往会存在一些特殊字符的应用或者会使用某些较为复杂的测量单位。
2、系统不支持批量证书更新以及批量打印等功能,在常见的场景中,出具证书是需要进行批量导出的过程。
3、无法满足实时打印预览或者PDF预览,这样直至打印前都无法确定打印的格式、范围等是否符合需求。
在这篇分享中,我们将帮助大家着重解决两个问题:
1、在浏览器中生成PDF文件;
2、解决中文以及特殊字符导出PDF乱码的问题。
在浏览器中生成PDF文件。前端生成PDF文件纯依赖于客户端的浏览器资源,对于不同的终端,导出PDF的难度会比服务端有所增加。市面上主流的浏览器有三四家,例如Chrome、Safari、FireFox等,每个浏览器对于文字内容、CSS属性处理都不一致,有可能某些配置在某个浏览器上可行,换了一个浏览器之后就有可能天差地别。另外,对于原生的PDF文件来说,仅包含英文字体,不包含任何中文字体,因此当导出的内容中含有中文字体编码时,就会显示乱码,所以通常情况下,我们都需要为PDF进行字体注册操作。
目前常用的前端生成PDF文件的方法大致有以下几种。
1、HTML2Canvas的方法将HTML 转换成图片后,在将图转PDF文件。这种方法比较适合单一页面。
2、jsPDF 直接H5转成PDF。
除了上述的方案之后,使用SpreadJS直接在线设计布局,并且可以直接生成PDF文件。 带来的好处是什么呢?可视化的操作、代码量少并且可以适配不同的浏览器环境。当然也会有一定的缺点,对于字体较多的文件,需要注册不同的字体,字体文件越大,占用的带宽就越大。另外,当文件比较大的时候,有可能会存在性能问题,不过这个也几乎是前端导出PDF文件的一个瓶颈。那么较为理想的方案便是可以在前端(SpreadJS)设计、展示,最后交由后端来单独导出或者批量导出。
介绍了那么多,我们还是回到本篇文章的主题,如何通过前端来生成PDF文件。需要用到SpreadJS以及导出PDF相关的功能,首先需要在页面上引入相关的资源。
然后创建一个用于承载表格实例的DOM。
初始化表格控件并加载已设计好的表单,或者也可以通过setValue的接口实现简单的赋值操作。
想要表单按照指定的要求导出,可以通过代码设置打印相关的配置,也可以用设计器来进行设计。下面是配置打印信息相关的代码。
最后,通过调用savePDF方法,将工作簿对象转为blob,我们可以通过window.open来进行pdf的预览或者通过一些保存文件的插件直接将这个blob保存为PDF文件。
这是open之后的效果,我们可以直接通过浏览器导出PDF文件或者是调用浏览器的打印接口实现打印。
正如前面所说的,在国内,使用中文的报告是一件再常见不过的事,在计量检测等相关场景,特殊字符的使用也较多。在没注册对应的字体之前,导出的中文字体和特殊字体都显示的是乱码。因此,还需要处理导出中文以及特殊字符PDF乱码的问题。
前面提到了注册字体,那我们的字体应该怎么来?要什么格式的字体呢?首先,先确认我们的表单需要用到哪些字体,然后去找对应字体的ttf文件(电脑上或者是一些字体网站上都有,需注意版权问题)。找到之后将其转为base64格式的文件。具体如何转,可以找一些在线的文件转换器,不过在线的有可能会因为字体文件太大而崩溃,或者有能力的大佬可以自己写一个转换的工具。然后通过下面的方式去把我们的字体文件存储为一个js文件放到我们的项目中。
初始化表单这些就和上面的操作基本一致了,下面就是关键的注册字体步骤了。我们定义了一个font对象,里面只定义了常规(normal)的字体,里面的simkai.ttf就是我们上面的创建的字体文件。
还有一点需要注意的是,虽然注册了字体,但是要设置对应的中文字体。或者换过来说,你需要在表单上设置什么字体,就去注册对应的字体。
那我们再来看看特殊字符,注册字体与中文字体的步骤是一致的,特殊在于为了想要在页面上显示特殊字符,我们需要通过css的font-face来指定一个font-family。例如创建了一个叫sunway-font的特殊字体,想要在页面上显示。
最后就是通过savePDF方法导出PDF文件,可以看到PDF的中文和特殊字符都可以正常显示。
怎么样?学“废”了吗?不妨试试SpreadJS,“卷”起来。
我们为什么要写个组件上传到npm镜像上呢,我们肯定遇到过这样一个场景,项目中有很多地方与某个功能相似,你想到的肯定是把该功能封装成Component组件,后续方便我们调用。但是过了一段时间,你的Leader让你去开发另一个项目,结果你在哪个项目中又看见了类似的功能,你这时会怎么做? 你也可以使用Ctrl + c + v大法,拿过来上一个项目封装好的代码,但是如果需求有些变动,你得维护两套项目的代码,甚至以后更多的项目....,这时你就可以封装一个功能上传到你们公司内网的npm上(或者自己的账号上),这样每次遇到类似的功能直接npm install 安装import导入进来使用就可以,需求有变动时完全可以改动一处代码。
笔者这里使用的是Webpack配置(有点菜,不要介意),也可以安装一个Vue-cli简单版的,它那里面有暴露Webpack的配置(也得修改自行配置),我们来配置一下打包组件环境,一般开发组件库都是使用的umd格式,这种格式支持Es Module、CommonJs、AMD三种引入方式使用,主要就是Webpack里的library和libraryTarget,如果不明白的看这里详解webpack的out.libraryTarget属性
我这里的Webpack版本为4, 最好跟着本章里的插件版本号进行安装,避免出现版本兼容问题
|- /node_modules
|- /src
|- Tag.vue
|- main.js
|- index.html
|- webpack.config.js
|- package.json
复制代码
npm init -y
复制代码
cnpm i webpack webpack-cli -D
cnpm i css-loader style-loader -D
cnpm i file-loader -D
cnpm i vue-loader@15.7.0 vue vue-template-compiler -D
cnpm i html-webpack-plugin@3.2.0 -D
复制代码
const VueLoaderPlugin=require('vue-loader/lib/plugin')
const HtmlWebpackPlugin=require("html-webpack-plugin")
module.exports={
mode: "development",
entry: "./src/main.js",
output: {
filename: "index.js"
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.(ttf|eot|woff|svg|woff2)/,
use: "file-loader"
},
{
test: /\.vue$/,
use: "vue-loader"
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: "./index.html"
})
]
}
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
</html>
复制代码
以上我们基本环境就搭建完啦,可以在终端使用npx webpack运行看看哦。
我这里只做一个示例哈,代码就不写那么复杂,大家知道怎么打包使用就行,具体封装成啥样看你们公司需求啦~。笔者这里使用Element Ui组件来做一个示例,相信大部分小伙伴公司也在使用Element Ui。假如我们项目中有以下类似的功能就可以单独封装起来。
import Vue from 'vue'
import { Tag } from 'element-ui';
import 'element-ui/lib/theme-chalk/tag.css';
import customTag from "./Tag.vue"
Vue.component(Tag.name, Tag)
export default customTag
复制代码
<template>
<div class="Tag">
{{ msg }}
<el-tag type="success">标签二</el-tag>
</div>
</template>
<script>
export default {
name: 'Tag',
data() {
return {
msg: "hello 蛙人",
}
},
created() {
},
components: {},
watch: {},
methods: {
}
}
</script>
<style scoped>
</style>
复制代码
将webpack.config.js里的output修改为如下
output: {
filename: "index.js",
library: "Modal",
libraryTarget: "umd"
}
复制代码
配置完之后就可以使用npx webpack打包,可以看到有一个dist目录,该目录下存在一个index.js, 这个文件就是我们封装的Tag.vue文件, 你可以将它引入到你的项目中,进行调用,该文件支持Es Module、CommonJs、AMD三种方式引入。
import Vue from 'vue'
import { Tag } from 'element-ui';
import 'element-ui/lib/theme-chalk/tag.css';
Vue.component(Tag.name, Tag)
import CustomTag from "./index" // 打包完的,直接引入进来
new Vue({
el: "#app",
render: h=> h(CustomTag)
})
复制代码
如果没有npm账号呢,先去官网注册一个npm账号这里
在终端执行npm init -y ,进行初始package.json文件,主要信息就是name和main字段,前者是这个包的名称(也就是npm instal xxx),后者则是我们打包好的文件Tag文件,默认main就去找这个入口文件。
注意:包名称不能包含大写,包名称不能包含大写,包名称不能包含大写,重要的事情说三遍
{
"name": "custom-tag-waren",
"version": "1.0.0",
"description": "这是xxxx",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "WaRen",
"license": "ISC"
}
复制代码
如果淘宝镜像之前被更改,先改回来执行以下命令
npm config set registry http://registry.npmjs.org
复制代码
注册完之后,执行npm login, 依次填写你的用户名、密码、邮箱
执行npm publish发布,然后等待进度条完成即可。
这是因为镜像设置成淘宝镜像了,设置回来即可
no_perms Private mode enable, only admin can publish this module
复制代码
一般是没有登录,重新登录一下 npm login 即可
npm publish failed put 500 unexpected status code 401
复制代码
包名被占用,改个包名即可,最好在官网查一下是否有包名被占用,之后再重命名
npm ERR! you do not have permission to publish “your module name”. Are you logged in as the correct user?
复制代码
邮箱未验证,去官网验证一下邮箱
you must verify your email before publishing a new package
复制代码
cnpm i custom-tag-waren -D
复制代码
import Vue from 'vue'
import { Tag } from 'element-ui';
import 'element-ui/lib/theme-chalk/tag.css';
import customTagWaren from "custom-tag-waren" // 下载完引入进来
Vue.component(Tag.name, Tag)
new Vue({
el: "#app",
render: h=> h(customTagWaren)
})
复制代码
到此为止就完成了一个组件的打包上传下载,这样我们在每个项目需要的时候直接npm install安装就行,当需求改动的时候只改一个文件然后再次发布就行。是不是很方便啦。
我们也不上传npm上,直接使用外链的形式使用,下面我们来看看
<template>
<div class="Tag">
<TagEl/>
</div>
</template>
<script>
import TagEl from "./index"
export default {
name: 'Tag',
data() {
return {
}
},
components: {
TagEl
},
}
</script>
<style scoped>
</style>
复制代码
上面example中,我们看到直接引入了index.js文件并进行注册组件,直接就可以使用啦。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<Tag/>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>
<script type="text/javascript" src="./dist/index.js"></script>
</body>
<script>
new Vue({
el: "#app",
components: {
Tag: Tag.default
}
})
</script>
</html>
复制代码
上面example中,直接使用script标签引入进来,也是注册完使用就可以。那么我们怎么知道他名字是Tag,这个你在封装组件的使用,必须指定Name名称。
*请认真填写需求信息,我们会在24小时内与您取得联系。