一、模板中用到的字体

1.水晶报表官方文档直接说明,如果需要导出PDF,则只支持ttf字体

2.在使用中发现,并非所有ttf字体都能导出PDF,如阿里巴巴普惠体。

由于对字体文件不熟悉,目前只知道在读取字体文件时,会查询一堆Table。

其中的ControlValueTable无法获取fontTableName,在此过程中抛出空指针异常,导致无法导出PDF。

3.初次读取报表模板文件时,优先从java运行时环境依赖下查询字体/jre/lib/font,次之是系统安装的字体。

读取字体到结构Map<String,Font>中,其中String为字体名称。

在读取某些中文字体时,key为null,发现此现象的字体是从某个字体站下载的汉仪字体包。

4.除了以上字体外,目前使用的大部分ttf中文字体都可以正常导出PDF,如微软雅黑、楷体、宋体、文泉驿正黑等,但是会出现第二条问题,如下所示。

二、chrome浏览器无法显示个别中字

1.直接从浏览器打印也会打印空白字

2.不同字体不显示的字不同,尚未发现能完美显示所有字的中文字体

3.Firefox和IE正常显示所有字

4.从chrome保存到本地后,使用其他PDF编辑器(如acrobat)打开,所有字正常显示

5.从chrome中的PDF空白字处复制不显示的字符,到本地文本编辑器(如NotePad++)可以正常显示该字

6.使用水晶报表编辑工具SAP Crystal Reports无论是预览还是导出PDF均可以正常显示所有字

7.当模板中配置了【思源黑体.otf】且系统和jre/lib/font下均不存在阿里巴巴普惠体时,

输出PDF到chrome浏览器可以显示所有字,但是点击下载或打印会导致浏览器崩溃。

偶尔可以下载成功,下载得到的PDF使用本地其他PDF编辑器打开会导致编辑器崩溃。

解决方案:

1.升级chrome至79.0.3945.88或以上,基本上可以确定是chrome对各个版本的pdf兼容性有自己的想法导致的问题。

2.先把水晶报表生成的PDF转成图片,再将图片嵌入新的PDF文档,最终将新的PDF文档输出至浏览器。

实例如下所示:

1.pdfbox

引入第三方库。

<!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox -->
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox-tools -->
<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox-tools</artifactId>
    <version>2.0.18</version>
</dependency>

2.PDF转图片

非最佳实现,仅供参考。

PDDocument originDocument = PDDocument.load(reportStream);
PDFRenderer originRender = new PDFRenderer(originDocument);
String rootPath = new File(".").getAbsolutePath() + "/tmp_report_";
int pageSize = originDocument.getNumberOfPages();
try (PDDocument targetDocument = new PDDocument()) {
    for (int pageNum = 0; pageNum < pageSize; ++pageNum) {
        // 将一页转为图片
        String filePath = rootPath + pageNum + ".png";
        BufferedImage bufferedImage = originRender.renderImageWithDPI(pageNum, REPORT_DPI, ImageType.GRAY);
        // int REPORT_DPI = 300
        ImageIOUtil.writeImage(bufferedImage, filePath, REPORT_DPI);
        // 载入图片并转为PDF图像对象
        PDImageXObject pdImage = PDImageXObject.createFromFile(filePath, targetDocument);
        PDPage page = new PDPage();
        // 创建新的PDF文档并添加图片
        targetDocument.addPage(page);
        PDPageContentStream content = new PDPageContentStream(targetDocument, page);
        content.drawImage(pdImage, 0, 0,  page.getMediaBox().getWidth(),    
                          page.getMediaBox().getHeight());
        content.close();
        Files.deleteIfExists(Paths.get(filePath));
    }
    // 将新的PDF文档输出到响应流
    targetDocument.save(response.getOutputStream());
} catch (Exception e) {
    LoggerUtil.getLogger().error("PDF渲染失败", e);
}

值得注意是:dpi、图片格式、渲染类型。

  • dpi设为300,保证清晰度
  • 图片格式为png
  • 渲染类型为ImageType.GRAY

经过测试,后两个值若为jpg+ImageType.RGB等其他组合,则经过图片转换的PDF大小会增长很多(数倍之多)。

故建议设为png+ImageType.GRAY的组合,或有测试过更好的方式欢迎指正。

除此之外,将生成的图片嵌入新的PDF文档后,存在图片比例畸变的问题。

content.drawImage(pdImage, 0, 0,  page.getMediaBox().getWidth(),    
                       page.getMediaBox().getHeight());

畸变比较小,由于测试PDF内容存在圆形图片水印才被发现,可根据实际显示情况进行微调。

content.drawImage(pdImage, REPORT_X_OFFSET, 0,
                        page.getMediaBox().getWidth() - REPORT_WIDTH_OFFSET, 
                        page.getMediaBox().getHeight());

畸变调整前:

安装水晶报表提示java update错误 水晶报表java生成pdf_chrome

畸变调整后:

安装水晶报表提示java update错误 水晶报表java生成pdf_字段_02

三、Java bean作为数据源

1.修改CRConfig.xml配置,在SAP Crystal Reports2016中,需要修改:

  • JavaDir32
  • JavaDir64
  • JavaBeansClassPath

2.通过阅读官方说明文档,Bean文件需要编译为.class文件或打包为.jar文件。

.class需配置JavaBeansClassPath为路径,.jar需配置JavaBeansClassPath为路径+xxx.jar。

3.bean文件中需要包含返回类型为java.sql.ResultSet的方法,不可以返回null,不可以是不能正常运行的SQL,否则在报表编辑器中连接时均无法连接。一个示例如下:

public ResultSet getDoorBean() throws ReportSDKException {
    List<DoorBean> doorItems = new ArrayList<>();
    POJOResultSetHelper helper = new POJOResultSetHelper(DoorBean.class);
    return helper.createResultSet(doorItems);
}

其中,getDoorBean会成为编辑器中数据源连接的表名,也是读取并注入数据源时需要写入的表名。

4.编译为.class文件并且正确配置JavaBeansClassPath后,打开SAP Crystal Reports2016,选择Java bean connectivity,在下拉框中应当出现javaservertype=javabeans javabeanname=的字样。

并非直接出现bean的名称,只有连接成功后下拉框中才会出现bean名称列表。

成功连接后,bean字段与jdbc方式获取的字段一样,可以拖拽到模板中。

5.如果bean字段修改,可以通过【验证数据库】操作进行字段更新,避免重新连接数据源导致重新配置字段,验证的字段通过名称进行比对映射。

四、图片

1.暂时未发现使用相对路径读取到图片的方法

2.使用绝对路径可以读取图片

3.使用URL可以读取图片,但是官方论坛的答复是:不支持https

4.可以通过公式字段获取模板的绝对路径,再通过模板与图片的相对位置关系获得图片路径,如下所示:

Left(Filename,length(filename) - Length(Mid (Filename ,InstrRev (Filename,"\") + 1) ))

但是不解决问题,因为使用tomcat运行后端时,读取一次模板文件,就默认在tomcat/temp下生成临时模板数据。

此时获取到的模板路径是tomcat/temp,而非项目路径。

5.关于模板文件

在本地运行中发现,如果删除tomcat路径下的temp文件夹,临时文件被生成到idea intellij的安装路径下的temp文件夹中。

论坛的答复是,只要ReportClientDocument流被close,临时文件就会被删除,实际上并没有。

6.透明背景


Crystal Reports converts images to .bmp which unfortunately does not support transparency for images.