本人菜鸟一枚,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开头的版本之后更狠,每页的上方都会添加。。。水印样式如下

spire java在线 spire.doc for java 报错_i++

 基于此,我使用了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的地方乱七八糟的:

spire java在线 spire.doc for java 报错_Source_02

 图里可以看见,有的没加上,有的加歪了等等。复查代码,发现原来是坐标问题,原代码是以第一张图的尺寸为准,后面每页都按照这个尺寸加,自然就乱了。改进代码:

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

再次运行,结果很完美。

spire java在线 spire.doc for java 报错_Source_03

 本地windows环境没问题,于是兴高采烈的部署在了linux环境的服务器中,结果。。。

spire java在线 spire.doc for java 报错_spring boot_04

 报错信息如下:

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:萌新很菜,花了大半天才勉强摸到门路,代码也杂乱不堪,大佬们轻点喷。。。