本人菜鸟一枚,1年java后端。一日有如下需求:在已经实现了Excel转PDF再转jpg(文件预览)的基础上,将Excel中每个sheet的名字标在每页PDF/jpg上。Excel转PDF转jpg的内容不做赘述,从新需求开始入手讲述。
先百度找了波,发现了spire系列的工具比较符合我的要求。spire for java,这是我选择的工具,其他的spire for office啥的没试过,大致应该都差不多。解决步骤分成两部分,先获取每个sheet的名字,再把这些字符串写在PDF上。第一步的代码在此网址下找到:
怎么利用Aspose.Cells 获取excel 数据表中sheet的名称
此方法涉及到的aspose cells插件不做赘述,因为我在其他地方正好有用到这个插件,直接拿来复用了。在那个网址里扒来的代码比较混乱,重新整理一下。
// 验证 License
getLicense();
Workbook wb = new Workbook(excelFilePath);
//获取Excel每个sheet名
List<ComboModel> myList = new ArrayList<>();
WorksheetCollection myColection = wb.getWorksheets();
int k1 = myColection.getCount();
for (int i = 0; i < k1; i++) {
ComboModel model = new ComboModel();
model.setId(i);
model.setText(myColection.get(i).getName());
myList.add(model);
}
这是实体ComboModel的代码:
import io.swagger.annotations.ApiModel;
import lombok.Data;
@Data
@ApiModel("ComboModel")
public class ComboModel {
public Integer id;
public String text;
}
这块没啥说的,没遇到问题。
接下来就是把刚刚获取到的每个sheet名(上述代码中的“myList”)写入PDF了。
原文是给PDF加页码,我把页码换成sheet名,不过分吧?跳过Excel转PDF功能的代码,直接从转出的PDF文件开始。改后的代码如下:
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile(pdfFilePath);
//添加一个空白页,目的为了删除jar包添加的水印,后面再移除这一页
pdf.getPages().add();
//获取页面尺寸
Dimension2D pageSize = pdf.getPages().get(0).getSize();
//初始化y坐标
float y = (float) pageSize.getHeight() - 40;
//创建字体
PdfTrueTypeFont font = new PdfTrueTypeFont(new Font("宋体", Font.PLAIN, 10), true);
//遍历文档中的页
for (int i = 0; i < pdf.getPages().getCount(); i++) {
//创建复合域
PdfCompositeField compositeField = new PdfCompositeField(font, PdfBrushes.getBlack(), myList.get(i).getText());
//设置复合域内文字对齐方式
compositeField.setStringFormat(new PdfStringFormat(PdfTextAlignment.Right, PdfVerticalAlignment.Top));
//测量文字大小
Dimension2D textSize = font.measureString(compositeField.getText());
//设置复合域的在PDF页面上的位置及大小
compositeField.setBounds(new Rectangle2D.Float(((float) pageSize.getWidth() - (float) textSize.getWidth()) / 2, y, (float) textSize.getWidth(), (float) textSize.getHeight()));
//将复合域添加到PDF页面
compositeField.draw(pdf.getPages().get(i).getCanvas());
}
//移除第一个页
pdf.getPages().remove(pdf.getPages().get(pdf.getPages().getCount() - 1));
//保存为另外一个文档
pdf.saveToFile(pdfFilePath);
从代码可以看到,加了个步骤,处理前添加一页,处理后删除一页,原因是spire这个插件不免费,会在第一页的最上方加上一段“水印”,这个情况在4开头的版本之前的版本出现,而在5开头的版本之后更狠,每页的上方都会添加。。。水印样式如下
基于此,我使用了4开头的版本,只需要解决第一页的水印即可。各版本可在此网站下载:
Nexus Repository Manager
解决了水印问题,在本地运行,发现会报错,说是超出范围了
java.lang.IndexOutOfBoundsException: Index: 7, Size: 7
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
at com.*.fileview.utils.ExcelPdfUtils.excel2pdf(ExcelPdfUtils.java:109)
at com.*.fileview.utils.ExcelPdfUtils.excel2pdf(ExcelPdfUtils.java:57)
at com.*.fileview.TestMain.main(TestMain.java:75)
仔细回想,发现是因为添加了一页,导致页码整体要+1,做些修改,把 i 做个判断,让多加的那页不影响后面的页:
for (int i = 0; i < pdf.getPages().getCount(); i++) {
int ii = i == 0 ? i : i - 1;
//创建复合域
PdfCompositeField compositeField = new PdfCompositeField(font, PdfBrushes.getBlack(), myList.get(ii).getText());
//设置复合域内文字对齐方式
compositeField.setStringFormat(new PdfStringFormat(PdfTextAlignment.Right, PdfVerticalAlignment.Top));
//测量文字大小
Dimension2D textSize = font.measureString(compositeField.getText());
//设置复合域的在PDF页面上的位置及大小
compositeField.setBounds(new Rectangle2D.Float(((float) pageSize.getWidth() - (float) textSize.getWidth()) / 2, y, (float) textSize.getWidth(), (float) textSize.getHeight()));
//将复合域添加到PDF页面
compositeField.draw(pdf.getPages().get(ii).getCanvas());
}
改完后运行,发现成功了,但是PDF写入sheet的地方乱七八糟的:
图里可以看见,有的没加上,有的加歪了等等。复查代码,发现原来是坐标问题,原代码是以第一张图的尺寸为准,后面每页都按照这个尺寸加,自然就乱了。改进代码:
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile(pdfFilePath);
//添加一个空白页,目的为了删除jar包添加的水印,后面再移除这一页
pdf.getPages().add();
//创建字体
PdfTrueTypeFont font = new PdfTrueTypeFont(new Font("宋体", Font.PLAIN, 10), true);
Dimension2D pageSize;
float y;
//遍历文档中的页
for (int i = 0; i < pdf.getPages().getCount(); i++) {
int ii = i == 0 ? i : i - 1;
//获取页面尺寸
pageSize = pdf.getPages().get(ii).getSize();
//初始化y坐标
y = (float) pageSize.getHeight() - 40;
//创建复合域
PdfCompositeField compositeField = new PdfCompositeField(font, PdfBrushes.getBlack(), myList.get(ii).getText());
//设置复合域内文字对齐方式
compositeField.setStringFormat(new PdfStringFormat(PdfTextAlignment.Right, PdfVerticalAlignment.Top));
//测量文字大小
Dimension2D textSize = font.measureString(compositeField.getText());
//设置复合域的在PDF页面上的位置及大小
compositeField.setBounds(new Rectangle2D.Float(((float) pageSize.getWidth() - (float) textSize.getWidth()) / 2, y, (float) textSize.getWidth(), (float) textSize.getHeight()));
//将复合域添加到PDF页面
compositeField.draw(pdf.getPages().get(ii).getCanvas());
}
//移除第一个页
pdf.getPages().remove(pdf.getPages().get(pdf.getPages().getCount() - 1));
//保存为另外一个文档
pdf.saveToFile(pdfFilePath);
再次运行,结果很完美。
本地windows环境没问题,于是兴高采烈的部署在了linux环境的服务器中,结果。。。
报错信息如下:
class com.spire.pdf.packages.sprxKA: Fail to save. Saving of requested font type is not supported.
com.spire.pdf.packages.sprUKA.spr (Unknown Source)
com.spire.pdf.graphics.PdfTrueTypeFont.<init>(Unknown Source)
com.spire.pdf.graphics.PdfTrueTypeFont.<init>(Unknown Source)
com.spire.pdf.graphics.PdfTrueTypeFont.<init>(Unknown Source)
com.*.fileview.utils.ExcelPdfUtils.excel2pdf(ExcelPdfUtils.java:101)
com.*.fileview.utils.ExcelPdfUtils.excel2pdf(ExcelPdfUtils.java:57)
com.*.fileview.service.FileHandlerService.justLoad(FileHandlerService.java:431)
com.*.fileview.service.FileConvertQueueTask$ConvertTask.run(FileConvertQueueTask.java:78)
java.lang.Thread.run(Thread.java:748)
at com.spire.pdf.packages.sprUKA.spr (Unknown Source)
at com.spire.pdf.graphics.PdfTrueTypeFont.<init>(Unknown Source)
at com.spire.pdf.graphics.PdfTrueTypeFont.<init>(Unknown Source)
at com.spire.pdf.graphics.PdfTrueTypeFont.<init>(Unknown Source)
at com.*.fileview.utils.ExcelPdfUtils.excel2pdf(ExcelPdfUtils.java:101)
at com.*.fileview.utils.ExcelPdfUtils.excel2pdf(ExcelPdfUtils.java:57)
at com.*.fileview.service.FileHandlerService.justLoad(FileHandlerService.java:431)
at com.*.fileview.service.FileConvertQueueTask$ConvertTask.run(FileConvertQueueTask.java:78)
at java.lang.Thread.run(Thread.java:748)
于是上百度寻找解决方法,网上基本上没有对此问题的解答。因为报错中提到了“font”,所以很多网上的回答都说是缺字体什么的鬼话。于是,在某个网址中,我找到了一个让我灵光一闪的点:
虽然我的问题不是无法显示中文,但是我注意到代码中关于字体的配置里,有“宋体”两个汉字,会不会是因为这个服务器没法识别?根据那个网站里说的,“Microsoft Yahei”(微软雅黑)可以显示中文,那我索性把宋体改成这个试试。
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile(pdfFilePath);
//添加一个空白页,目的为了删除jar包添加的水印,后面再移除这一页
pdf.getPages().add();
//创建字体
PdfTrueTypeFont font = new PdfTrueTypeFont(new Font("Microsoft Yahei", Font.PLAIN, 10), true);
Dimension2D pageSize;
float y;
//遍历文档中的页
for (int i = 0; i < pdf.getPages().getCount(); i++) {
int ii = i == 0 ? i : i - 1;
//获取页面尺寸
pageSize = pdf.getPages().get(ii).getSize();
//初始化y坐标
y = (float) pageSize.getHeight() - 40;
//创建复合域
PdfCompositeField compositeField = new PdfCompositeField(font, PdfBrushes.getBlack(), myList.get(ii).getText());
//设置复合域内文字对齐方式
compositeField.setStringFormat(new PdfStringFormat(PdfTextAlignment.Right, PdfVerticalAlignment.Top));
//测量文字大小
Dimension2D textSize = font.measureString(compositeField.getText());
//设置复合域的在PDF页面上的位置及大小
compositeField.setBounds(new Rectangle2D.Float(((float) pageSize.getWidth() - (float) textSize.getWidth()) / 2, y, (float) textSize.getWidth(), (float) textSize.getHeight()));
//将复合域添加到PDF页面
compositeField.draw(pdf.getPages().get(ii).getCanvas());
}
//移除第一个页
pdf.getPages().remove(pdf.getPages().get(pdf.getPages().getCount() - 1));
//保存为另外一个文档
pdf.saveToFile(pdfFilePath);
于服务器运行,完美运行,至此达到了目的。
ps:萌新很菜,花了大半天才勉强摸到门路,代码也杂乱不堪,大佬们轻点喷。。。