admin管理员组文章数量:1648973
java将ftl转换成pdf,vue对转换的pdf进行预览、打印、下载
- 1、ftl模板依赖
- 2、ftl转换pdf
- 2.1、flt转pdf字节数组核心代码块
- 2.2、得到ftl转换成pdf的字节数组
- 2.3、中文乱码处理
- 3、转换后的PDF添加水印和页脚
- 3.1 水印效果
- 3.2、水印及页脚添加核心代码块
- 4、vue预览
- 5、vue打印
- 5.1、核心代码块
- 5.2、中文乱码处理
1、ftl模板依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2、ftl转换pdf
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-core</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-pdfbox</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.openhtmltopdf</groupId>
<artifactId>openhtmltopdf-slf4j</artifactId>
<version>1.0.10</version>
</dependency>
2.1、flt转pdf字节数组核心代码块
// 模板加载
Configuration conf = new Configuration(Configuration.VERSION_2_3_23);
conf.setDefaultEncoding(StandardCharsets.UTF_8.name());
conf.setDateTimeFormat(TimeUtil.NORMAL_TIME_FORMAT);
conf.setClassForTemplateLoading(getClass(), "/template/");
try {
Template template = conf.getTemplate("scenePreview.ftl");
// 参数与模板生成html字符串
String htmlStr = FreeMarkerTemplateUtils.processTemplateIntoString(template, dto);
// html转换pdf
try(ByteArrayOutputStream os = new ByteArrayOutputStream()){
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.withHtmlContent(htmlStr, "");
// 读取系统字体 若没有对应字体 则会出现中文字体乱码问题
if (FileUtil.exist(fontUrl)) {
builder.useFont(new FSSupplier<InputStream>() {
@SneakyThrows
@Override
public InputStream supply() {
return Files.newInputStream(Paths.get(fontUrl));
}
}, fontName);
} else {
log.error("中文字体缺失,对应文件路径为:{}", fontUrl);
}
builder.toStream(os);
builder.run();
byte[] bytes = os.toByteArray();
return new ResponseEntity<>(bytes, HttpStatus.OK);
}
} catch (IOException e) {
log.error("凭证查看异常,", e);
} catch (TemplateException e) {
log.error("凭证查看模板异常,", e);
}
2.2、得到ftl转换成pdf的字节数组
2.3、中文乱码处理
-
上传windows7的中文字体库(SimSun.ttf)至指定路径
-
ftl设置中文名称
-
flt转换pdf指定字体配置
3、转换后的PDF添加水印和页脚
<!-- 页脚和水印 -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.27</version>
</dependency>
3.1 水印效果
3.2、水印及页脚添加核心代码块
/**
* 添加页脚和水印
*
* @param bytes 输入文件流对象
* @param waterContent 水印内容
* @return 响应结果
*/
private byte[] addFooter(byte[] bytes, String footerContent, String waterContent) throws IOException {
PDDocument document = PDDocument.load(bytes);
try(InputStream fontInput = new FileInputStream(fontUrl)){
PDFont font = PDType0Font.load(document, fontInput, false);
for (int i=0; i < document.getNumberOfPages(); i++) {
PDPage page = document.getPage(i);
PDPageContentStream contentStream = new PDPageContentStream(document, page,
PDPageContentStream.AppendMode.APPEND, true, true);
// 设置字体和字号
contentStream.setFont(font, 6);
// 获取页面的大小
PDRectangle pageSize = page.getMediaBox();
// 计算文本的宽度
float textWidth = font.getStringWidth(footerContent) / 1000f * 6;
// 计算文本的位置(居中)
float startX = (pageSize.getWidth() - textWidth) / 2;
// 30为底部留白
float startY = pageSize.getLowerLeftY() + 20;
// 添加文本
contentStream.beginText();
contentStream.setTextMatrix(Matrix.getTranslateInstance(startX, startY));
contentStream.showText(footerContent);
contentStream.endText();
contentStream.close();
/**
* 添加水印
*/
PDPageContentStream contentStream2 = new PDPageContentStream(document, page,
PDPageContentStream.AppendMode.APPEND, true, true);
// 设置字体和字号
contentStream2.setFont(font, 6);
// 设置透明度
PDExtendedGraphicsState r = new PDExtendedGraphicsState();
r.setNonStrokingAlphaConstant(0.2f);
r.setAlphaSourceFlag(true);
contentStream2.setGraphicsStateParameters(r);
// 获取PDF页面大小
float pageHeight = page.getMediaBox().getHeight();
float pageWidth = page.getMediaBox().getWidth();
// 每页水印行数
int row = 6;
// 每行水印个数
int col = 6;
contentStream2.beginText();
for (int j=0; j<col; j++) {
for (int k=0; k<row; k++) {
float x = pageWidth / col * j + 90;
float y = pageHeight / row * k;
contentStream2.setTextMatrix(Matrix.getRotateInstance(0.4, x, y));
contentStream2.showText(waterContent);
}
}
contentStream2.endText();
contentStream2.close();
}
}
try(ByteArrayOutputStream baos = new ByteArrayOutputStream()){
document.save(baos);
document.close();
// 响应流
return baos.toByteArray();
}
}
4、vue预览
使用vue-pdf组件进行预览展示
5、vue打印
打印使用的vue-pdf的$refs.pdf.print打印方式
5.1、核心代码块
<div class="pagelist-con">
<el-button type="info" size="mini" @click="changePage(-1)">首页</el-button>
<el-button type="info" size="mini" @click="changePage(0)">上一页</el-button>
<span class="page-pading">
共 {{ pageCount }} 页 ,当前第 {{ currentPage }} 页
</span>
<el-button type="info" size="mini" @click="changePage(1)">下一页</el-button>
<el-button type="info" size="mini" @click="changePage(99999)">尾页</el-button>
</div>
<div class="print-btncon">
<el-button v-if="form.actionType === 'print'" size="mini" type="info"
icon="el-icon-printer" @click="$refs.pdf.print()">打印</el-button>
</div>
<div style="border: 1px solid #a0a0a0;overflow:auto;">
<div id="printFrom">
<pdf :src="pdfUrl" :page="currentPage" @progress="loadedRatio = $event" @num-pages="pageCount = $event"
@page-loaded="currentPage = $event" @loaded="loadPdfHandler" ref="pdf" class="pdf"></pdf>
</div>
</div>
changePage(val) {//翻页
if (val == 99999) {
this.currentPage = this.pageCount;
return;
}
if (val == -1) {
this.currentPage = 1;
return;
}
if (val === 0 && this.currentPage > 1) {
this.currentPage--;
}
if (val === 1 && this.currentPage < this.pageCount) {
this.currentPage++;
}
},
loadPdfHandler(e) {//加载分页信息
this.currentPage = 1; // 加载的时候先加载第一页
},
doAction() {
// TODO 获取后端的pdf字节流
},
5.2、中文乱码处理
将下面的代码保存成名称为pdfjsWrapper.js,然后将文件拖过去
替换node_modules\vue-pdf\src\pdfjsWrapper.js的文件,是替换文件,不是修改文件。
解决中文乱码的pdfjsWrapper.js文件内容如下:
import { PDFLinkService } from 'pdfjs-dist/es5/web/pdf_viewer';
/* 场景预览打印机打印预览乱码 则需要将该文件替换到本地dsep-page\node_modules\vue-pdf\src的路径下 替换原有的文件 */
var pendingOperation = Promise.resolve();
export default function(PDFJS) {
function isPDFDocumentLoadingTask(obj) {
return typeof(obj) === 'object' && obj !== null && obj.__PDFDocumentLoadingTask === true;
// or: return obj.constructor.name === 'PDFDocumentLoadingTask';
}
function createLoadingTask(src, options) {
var source;
if ( typeof(src) === 'string' )
source = { url: src };
else if ( src instanceof Uint8Array )
source = { data: src };
else if ( typeof(src) === 'object' && src !== null )
source = Object.assign({}, src);
else
throw new TypeError('invalid src type');
// source.verbosity = PDFJS.VerbosityLevel.INFOS;
// source.pdfBug = true;
// source.stopAtErrors = true;
if ( options && options.withCredentials )
source.withCredentials = options.withCredentials;
var loadingTask = PDFJS.getDocument(source);
loadingTask.__PDFDocumentLoadingTask = true; // since PDFDocumentLoadingTask is not public
if ( options && options.onPassword )
loadingTask.onPassword = options.onPassword;
if ( options && options.onProgress )
loadingTask.onProgress = options.onProgress;
return loadingTask;
}
function PDFJSWrapper(canvasElt, annotationLayerElt, emitEvent) {
var pdfDoc = null;
var pdfPage = null;
var pdfRender = null;
var canceling = false;
canvasElt.getContext('2d').save();
function clearCanvas() {
canvasElt.getContext('2d').clearRect(0, 0, canvasElt.width, canvasElt.height);
}
function clearAnnotations() {
while ( annotationLayerElt.firstChild )
annotationLayerElt.removeChild(annotationLayerElt.firstChild);
}
this.destroy = function() {
if ( pdfDoc === null )
return;
// Aborts all network requests and destroys worker.
pendingOperation = pdfDoc.destroy();
pdfDoc = null;
}
this.getResolutionScale = function() {
return canvasElt.offsetWidth / canvasElt.width;
}
this.printPage = function(dpi, pageNumberOnly) {
if ( pdfPage === null )
return;
// 1in == 72pt
// 1in == 96px
var PRINT_RESOLUTION = dpi === undefined ? 150 : dpi;
var PRINT_UNITS = PRINT_RESOLUTION / 72.0;
var CSS_UNITS = 96.0 / 72.0;
var printContainerElement = document.createElement('div');
printContainerElement.setAttribute('id', 'print-container')
function removePrintContainer() {
printContainerElement.parentNode.removeChild(printContainerElement);
}
new Promise(function(resolve, reject) {
printContainerElement.frameBorder = '0';
printContainerElement.scrolling = 'no';
printContainerElement.width = '0px;'
printContainerElement.height = '0px;'
printContainerElement.style.cssText = 'position: absolute; top: 0; left: 0';
window.document.body.appendChild(printContainerElement);
resolve(window)
})
.then(function(win) {
win.document.title = '';
return pdfDoc.getPage(1)
.then(function(page) {
var viewport = page.getViewport({ scale: 1 });
printContainerElement.appendChild(win.document.createElement('style')).textContent =
'@supports ((size:A4) and (size:1pt 1pt)) {' +
'@page { margin: 1pt; size: ' + ((viewport.width * PRINT_UNITS) / CSS_UNITS) + 'pt ' + ((viewport.height * PRINT_UNITS) / CSS_UNITS) + 'pt; }' +
'}' +
'@media print {' +
'body { margin: 0 }' +
'#print-canvas { page-break-before: avoid; page-break-after: always; page-break-inside: avoid; display: block }' +
'body > *:not(#print-container) { display: none; }' +
'}'+
'@media screen {' +
'body { margin: 0 }' +
'}'
return win;
})
})
.then(function(win) {
var allPages = [];
for ( var pageNumber = 1; pageNumber <= pdfDoc.numPages; ++pageNumber ) {
if ( pageNumberOnly !== undefined && pageNumberOnly.indexOf(pageNumber) === -1 )
continue;
allPages.push(
pdfDoc.getPage(pageNumber)
.then(function(page) {
var viewport = page.getViewport({ scale: 1 });
var printCanvasElt = printContainerElement.appendChild(win.document.createElement('canvas'));
printCanvasElt.setAttribute('id', 'print-canvas')
printCanvasElt.width = (viewport.width * PRINT_UNITS);
printCanvasElt.height = (viewport.height * PRINT_UNITS);
return page.render({
canvasContext: printCanvasElt.getContext('2d'),
transform: [ // Additional transform, applied just before viewport transform.
PRINT_UNITS, 0, 0,
PRINT_UNITS, 0, 0
],
viewport: viewport,
intent: 'print'
}).promise;
})
);
}
Promise.all(allPages)
.then(function() {
win.focus(); // Required for IE
if (win.document.queryCommandSupported('print')) {
win.document.execCommand('print', false, null);
} else {
win.print();
}
removePrintContainer();
})
.catch(function(err) {
removePrintContainer();
emitEvent('error', err);
})
})
}
this.renderPage = function(rotate) {
if ( pdfRender !== null ) {
if ( canceling )
return;
canceling = true;
pdfRender.cancel().catch(function(err) {
emitEvent('error', err);
});
return;
}
if ( pdfPage === null )
return;
var pageRotate = (pdfPage.rotate === undefined ? 0 : pdfPage.rotate) + (rotate === undefined ? 0 : rotate);
var scale = canvasElt.offsetWidth / pdfPage.getViewport({ scale: 1 }).width * (window.devicePixelRatio || 1);
var viewport = pdfPage.getViewport({ scale: scale, rotation:pageRotate });
emitEvent('page-size', viewport.width, viewport.height, scale);
canvasElt.width = viewport.width;
canvasElt.height = viewport.height;
pdfRender = pdfPage.render({
canvasContext: canvasElt.getContext('2d'),
viewport: viewport
});
annotationLayerElt.style.visibility = 'hidden';
clearAnnotations();
var viewer = {
scrollPageIntoView: function(params) {
emitEvent('link-clicked', params.pageNumber)
},
};
var linkService = new PDFLinkService();
linkService.setDocument(pdfDoc);
linkService.setViewer(viewer);
pendingOperation = pendingOperation.then(function() {
var getAnnotationsOperation =
pdfPage.getAnnotations({ intent: 'display' })
.then(function(annotations) {
PDFJS.AnnotationLayer.render({
viewport: viewport.clone({ dontFlip: true }),
div: annotationLayerElt,
annotations: annotations,
page: pdfPage,
linkService: linkService,
renderInteractiveForms: false
});
});
var pdfRenderOperation =
pdfRender.promise
.then(function() {
annotationLayerElt.style.visibility = '';
canceling = false;
pdfRender = null;
})
.catch(function(err) {
pdfRender = null;
if ( err instanceof PDFJS.RenderingCancelledException ) {
canceling = false;
this.renderPage(rotate);
return;
}
emitEvent('error', err);
}.bind(this))
return Promise.all([getAnnotationsOperation, pdfRenderOperation]);
}.bind(this));
}
this.forEachPage = function(pageCallback) {
var numPages = pdfDoc.numPages;
(function next(pageNum) {
pdfDoc.getPage(pageNum)
.then(pageCallback)
.then(function() {
if ( ++pageNum <= numPages )
next(pageNum);
})
})(1);
}
this.loadPage = function(pageNumber, rotate) {
pdfPage = null;
if ( pdfDoc === null )
return;
pendingOperation = pendingOperation.then(function() {
return pdfDoc.getPage(pageNumber);
})
.then(function(page) {
pdfPage = page;
this.renderPage(rotate);
emitEvent('page-loaded', page.pageNumber);
}.bind(this))
.catch(function(err) {
clearCanvas();
clearAnnotations();
emitEvent('error', err);
});
}
this.loadDocument = function(src) {
pdfDoc = null;
pdfPage = null;
emitEvent('num-pages', undefined);
if ( !src ) {
canvasElt.removeAttribute('width');
canvasElt.removeAttribute('height');
clearAnnotations();
return;
}
// wait for pending operation ends
pendingOperation = pendingOperation.then(function() {
var loadingTask;
if ( isPDFDocumentLoadingTask(src) ) {
if ( src.destroyed ) {
emitEvent('error', new Error('loadingTask has been destroyed'));
return
}
loadingTask = src;
} else {
loadingTask = createLoadingTask(src, {
onPassword: function(updatePassword, reason) {
var reasonStr;
switch (reason) {
case PDFJS.PasswordResponses.NEED_PASSWORD:
reasonStr = 'NEED_PASSWORD';
break;
case PDFJS.PasswordResponses.INCORRECT_PASSWORD:
reasonStr = 'INCORRECT_PASSWORD';
break;
}
emitEvent('password', updatePassword, reasonStr);
},
onProgress: function(status) {
var ratio = status.loaded / status.total;
emitEvent('progress', Math.min(ratio, 1));
}
});
}
return loadingTask.promise;
})
.then(function(pdf) {
pdfDoc = pdf;
emitEvent('num-pages', pdf.numPages);
emitEvent('loaded');
})
.catch(function(err) {
clearCanvas();
clearAnnotations();
emitEvent('error', err);
})
}
annotationLayerElt.style.transformOrigin = '0 0';
}
return {
createLoadingTask: createLoadingTask,
PDFJSWrapper: PDFJSWrapper,
}
}
效果展示
版权声明:本文标题:【java将ftl转换成pdf,vue对转换的pdf进行预览、打印、下载】 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1729504172a1203429.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论