现象

PDF教材导出到系统中,由程序将PDF转为图片后合并成一张大图供前端标注,但是在标注数学和化学学科的时候且源文件是PDF的情况下出现公式部分字符丢失的情况,如下图

原件

转换后效果

WTF!

转换方案

PDFBOX(当前方案)

public void pdf2Jpg(InputStream in, String jpgFilePath) {
int idx = jpgFilePath.lastIndexOf('.');
String jpgPrefix = StringUtils.substring(jpgFilePath, 0, idx) + File.separator;
int pdfDpi = 200;
try (final PDDocument document = Loader.loadPDF(in)) {
int size = document.getNumberOfPages();
for (int i = 0; i < size; i++) {
BufferedImage image = new PDFRenderer(document).renderImageWithDPI(i, pdfDpi, ImageType.RGB);
File dir = new File(jpgPrefix);
if (!dir.exists()) {
dir.mkdirs();
}
File jpgFile = new File(jpgPrefix + i + ".jpg");
ImageIO.write(image, "jpg", jpgFile);
}
} catch (Exception e) {
e.printStackTrace();
}
}

转换效果如下



和别人发过来的问题截图一毛一样,起初我以为是字体问题,这个公式用的字体是 Cambria Math,我本地已经下载了该字体,但是从PDF中复制出来的字体是CambriaMath 不带空格,这一块触及知识盲区了,为啥不带空格? 复制到Word中显示的就是一个长方形里面有个问好,我们换个SDK试下

icepdf

public static void pdf2Pic(String pdfPath, String path) throws IOException, PDFException, PDFSecurityException, InterruptedException {
org.icepdf.core.pobjects.Document document = new org.icepdf.core.pobjects.Document();
document.setFile(pdfPath);
//缩放比例
float scale = 2.5f;
//旋转角度
float rotation = 0f; for (int i = 0; i < document.getNumberOfPages(); i++) {
BufferedImage image = (BufferedImage)
document.getPageImage(i, GraphicsRenderingHints.SCREEN, org.icepdf.core.pobjects.Page.BOUNDARY_CROPBOX, rotation, scale);
RenderedImage rendImage = image;
try {
String imgName = i + ".jpg";
System.out.println(imgName);
File file = new File(path + imgName);
ImageIO.write(rendImage, "jpg", file);
} catch (IOException e) {
e.printStackTrace();
}
image.flush();
}
document.dispose();
}

转换效果如下



这下不是空白了,就是那个长方形内嵌问号的乱码,感觉就是字体问题,但是好像是由于公式引发的,这个是PDF,且我在word中手动输入的公式导出PDF再转图片是正常的,不明觉厉!我们再换个SDK试下

Aspose.word(有水印,仅测试)

public static void pdfToImage(InputStream inputStream, String imgFilePath) {
try {
log.info("convert pdf2jpg begin");
long old = System.currentTimeMillis();
Document pdfDocument = new Document(inputStream);
//分辨率
Resolution resolution = new Resolution(200);
JpegDevice jpegDevice = new JpegDevice(resolution);
List<BufferedImage> imageList = new ArrayList<BufferedImage>();
List<File> fileList = new ArrayList<>();
for (int index = 1; index <= pdfDocument.getPages().size(); index++) {
File file = new File(imgFilePath + index + ".jpg");
FileOutputStream fileOS = new FileOutputStream(file);
jpegDevice.process(pdfDocument.getPages().get_Item(index), fileOS);
fileOS.close();
imageList.add(ImageIO.read(file));
fileList.add(file);
}
//临时文件删除
long now = System.currentTimeMillis();
log.info("convert pdf2jpg completed, elapsed :" + ((now - old) / 1000.0) + "秒");
//删除临时文件
} catch (Exception e) {
e.printStackTrace();
log.error("convert pdf2jpg error:" + e);
} }

转换效果如下



看到aspose这个样子我基本上就确定,这个玩意和本地字体没有什么关系,惨不忍睹,再来

PDFRender,没错我已经用上PDFRender了

private static void pdfToImageRender(InputStream is, String imgFilePath) throws IOException {
int pagen = 1;
PDFFile pdffile = null;
byte[] byt = toByteArray(is);
try {
ByteBuffer buf = ByteBuffer.allocate(byt.length);
buf.put(byt);
pdffile = new PDFFile(buf);
} catch (Exception e) {
e.printStackTrace();
}
if (pagen > pdffile.getNumPages())
return;
pagen = pdffile.getNumPages(); for (int i = 1; i <= pagen; i++) {
PDFPage page = pdffile.getPage(i);
int width = (int) page.getBBox().getWidth();
int height = (int) page.getBBox().getHeight();
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = img.createGraphics();
PDFRenderer renderer = new PDFRenderer(page, g2,
new Rectangle(0, 0, width, height), null, Color.white);//这个color为渲染出来的图片的背景颜色
try {
page.waitForFinish();
} catch (Exception e) {
e.printStackTrace();
}
renderer.run();
g2.dispose();
OutputStream os = new FileOutputStream(imgFilePath + i + ".jpg");
try {
ImageIO.write(img, "jpg", os);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

转换效果如下

没错,你没看错,从第一个公式字符开始已经报错了,剩下的都没有渲染出来,这特喵的不还是字体问题,报错信息如下

java.lang.IllegalArgumentException: newPosition > limit: (1146308935 > 1619740)
at java.nio.Buffer.createPositionException(Buffer.java:269)
at java.nio.Buffer.position(Buffer.java:244)
at com.sun.pdfview.font.ttf.TrueTypeFont.parseDirectories(TrueTypeFont.java:312)
at com.sun.pdfview.font.ttf.TrueTypeFont.parseFont(TrueTypeFont.java:68)
at com.sun.pdfview.font.TTFFont.<init>(TTFFont.java:78)
at com.sun.pdfview.font.TTFFont.<init>(TTFFont.java:50)
at com.sun.pdfview.font.CIDFontType2.<init>(CIDFontType2.java:68)
at com.sun.pdfview.font.PDFFont.getFont(PDFFont.java:191)
at com.sun.pdfview.font.Type0Font.<init>(Type0Font.java:51)
at com.sun.pdfview.font.PDFFont.getFont(PDFFont.java:156)
at com.sun.pdfview.PDFParser.getFontFrom(PDFParser.java:1166)
at com.sun.pdfview.PDFParser.iterate(PDFParser.java:719)
at com.sun.pdfview.BaseWatchable.run(BaseWatchable.java:102)
at java.lang.Thread.run(Thread.java:748)
java.lang.IllegalArgumentException: newPosition > limit: (1146308935 > 1619740)
at java.nio.Buffer.createPositionException(Buffer.java:269)
at java.nio.Buffer.position(Buffer.java:244)
at com.sun.pdfview.font.ttf.TrueTypeFont.parseDirectories(TrueTypeFont.java:312)
at com.sun.pdfview.font.ttf.TrueTypeFont.parseFont(TrueTypeFont.java:68)
at com.sun.pdfview.font.TTFFont.<init>(TTFFont.java:78)
at com.sun.pdfview.font.TTFFont.<init>(TTFFont.java:50)
at com.sun.pdfview.font.CIDFontType2.<init>(CIDFontType2.java:68)
at com.sun.pdfview.font.PDFFont.getFont(PDFFont.java:191)
at com.sun.pdfview.font.Type0Font.<init>(Type0Font.java:51)
at com.sun.pdfview.font.PDFFont.getFont(PDFFont.java:156)
at com.sun.pdfview.PDFParser.getFontFrom(PDFParser.java:1166)
at com.sun.pdfview.PDFParser.iterate(PDFParser.java:719)
at com.sun.pdfview.BaseWatchable.run(BaseWatchable.java:102)
at java.lang.Thread.run(Thread.java:748)

小结

其实在PDFBOX转换的时候也有一条告警

00:19:53.100 [main] WARN org.apache.pdfbox.pdmodel.font.PDCIDFontType2 - Could not read embedded OTF for font DCWGQU+CambriaMath
java.io.IOException: head is mandatory
at org.apache.fontbox.ttf.TTFParser.parseTables(TTFParser.java:182)
at org.apache.fontbox.ttf.TTFParser.parse(TTFParser.java:150)
at org.apache.fontbox.ttf.OTFParser.parse(OTFParser.java:79)
at org.apache.fontbox.ttf.OTFParser.parse(OTFParser.java:27)
at org.apache.fontbox.ttf.TTFParser.parse(TTFParser.java:106)
at org.apache.fontbox.ttf.OTFParser.parse(OTFParser.java:73)
at org.apache.pdfbox.pdmodel.font.PDCIDFontType2.<init>(PDCIDFontType2.java:114)
at org.apache.pdfbox.pdmodel.font.PDCIDFontType2.<init>(PDCIDFontType2.java:67)
at org.apache.pdfbox.pdmodel.font.PDFontFactory.createDescendantFont(PDFontFactory.java:138)
at org.apache.pdfbox.pdmodel.font.PDType0Font.<init>(PDType0Font.java:88)
at org.apache.pdfbox.pdmodel.font.PDFontFactory.createFont(PDFontFactory.java:96)
at org.apache.pdfbox.pdmodel.PDResources.getFont(PDResources.java:143)
at org.apache.pdfbox.contentstream.operator.text.SetFontAndSize.process(SetFontAndSize.java:66)
at org.apache.pdfbox.contentstream.PDFStreamEngine.processOperator(PDFStreamEngine.java:849)
at org.apache.pdfbox.contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:495)
at org.apache.pdfbox.contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:469)
at org.apache.pdfbox.contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:142)
at org.apache.pdfbox.rendering.PageDrawer.drawPage(PageDrawer.java:264)
at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:338)
at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:259)
at org.apache.pdfbox.rendering.PDFRenderer.renderImageWithDPI(PDFRenderer.java:245)

查了一个pdfbox的报错信息,pdfbox不支持otf字体,如果要使用otf字体,则需要进行转换后方可使用,DCWGQU+CambriaMath这个关键词我在Google上面没有搜到

这里猜测应当是历史版本的office生成的公式,并且在转换PDF的时候把相关字体写入到PDF中去了,而这些个SDK都没有去读取和解析PDF自带的字体,只是读了个名称,然后再调用操作系统已安装的字体,字体不兼容解析失败导致异常,我目前的手上的工具也无法生成这样PDF来复现这个问题

其实我这边有拿到过其他有问题PDF的word原件,再用高版本的word导出PDF后用PDFBOX导出是没有问题的,但是这个并没有解决问题啊,主流的PDF转图片的SDK基本都不行,你总不能让源头客户去升级OFFICE再试试吧

一点感想

锁死了!就好像刺客五六七里何大春的 “情比金坚七天锁” ,一锁七天,不到时间无法打开。这个锁就像爱情一样,内部是打不开的,但是施锁的那个人如果能主动放弃其实是可以提前打开的,大春为了救阿七释放锁,我也可以,所以就释放JavaSDK,把搜索引擎后面的Java四个字母干掉,看看外面的世界

大杀器ImageMagick(开源,免费)

ImageMagick是一个免费的创建、编辑、合成图片的软件。它可以读取、转换、写入多种格式的图片。图片切割、颜色替换、各种效果的应用,图片的旋转、组合,文本,直线,多边形,椭圆,曲线,附加到图片伸展旋转。ImageMagick是免费软件:全部源码开放,可以自由使用,复制,修改,发布,它遵守GPL许可协议,可以运行于大多数的操作系统,ImageMagick的大多数功能的使用都来源于命令行工具

百度百科传送门:https://baike.baidu.com/item/ImageMagick/6865180?fr=aladdin

官网传送门:https://imagemagick.org/index.php

上代码

private static final boolean isWin = System.getProperty("os.name").toLowerCase().contains("win");

    private static final boolean isLinux = System.getProperty("os.name").toLowerCase().indexOf("linux") >= 0;
private static final String COMMAND_LINE_WIN = "C:/PROGRA~1/ImageMagick-7.1.0-Q16-HDRI/magick.exe convert -density 220 -quality 80 -background white -alpha remove {0} {1}/1.jpg";
private static final String COMMAND_LINE_LINUX = "magick convert -density 220 -quality 80 -background white -alpha remove {0} {1}/1.jpg"; public void pdf2jpgByMagick(String pdfPath, String jpgFilePath) throws IOException {
FileUtils.forceMkdir(new File(jpgFilePath));
String command;
if (isWin) {
command = MessageFormat.format(COMMAND_LINE_WIN, pdfPath, jpgFilePath);
}else if(isLinux){
command = MessageFormat.format(COMMAND_LINE_LINUX, pdfPath, jpgFilePath);
}else {
throw new RuntimeException("暂不支持的平台");
}
CommandUtil.exeCmd(command);
}

转换效果如下



那是相当的Perfect!但是windows安装(本地)、Linux服务器安装、还有容器部署镜像封装还有一堆事情,还有一些ImageMagick安装及转jpg参数设定的一些小坑,且看下回分享

PDF转图片部分公式字符丢失问题解决的爬坑记录的更多相关文章

  1. C# 给PDF添加图片背景

    C# 给PDF添加图片背景 今天要实现的是给PDF文件添加图片背景这个功能.PDF是近年来最流行的文件之一,无论是办公还是日常生活中都经常会用到,很多时候,PDF文件的背景色都是白色,看多了难免觉得累 ...

  2. HTML5将图片转化成字符画

    HTML5将图片转化成字符画 字符画大家一定非常熟悉了,那么如何把一张现有的图片转成字符画呢?HTML5让这个可能变成了现实,通过canvas,可以很轻松实现这个功能.其实原理很简单:扫描图片相应位置 ...

  3. Android ListView滑动过程中图片显示重复错乱闪烁问题解决

    最新内容建议直接访问原文:Android ListView滑动过程中图片显示重复错乱闪烁问题解决 主要分析Android ListView滚动过程中图片显示重复.错乱.闪烁的原因及解决方法,顺带提及L ...

  4. C# 第三方DLL,可以实现PDF转图片,支持32位系统、64位系统

    itextsharp.dll,是一个开源的在C#中用来生成PDF文档的库文件,不少C#爱好者用它制作出了PDF文档生成器.使用时只需在你的C#项目中添加引入此组件即可,使用方法网上有很多,自己查阅一下 ...

  5. PDF转图片 C# with Adobe API

    PDF转图片大概有十几种方式,褒贬不一,我就详细给大家说一下我认为效率最高的方式,使用Adobe官方的SDK 安装acrobat reader 9.0以上即可,勾选如下组件.

  6. 在线提取PDF中图片和文字

    无需下载软件,你就可以在线提取PDF中图片和文字,http://www.extractpdf.com/不仅可以获取本地PDF文档的图片和文字,还能获取远程PDF文档的图片和文字.如下图所示:结果本人测 ...

  7. 基于 canvas 将图片转化成字符画

    字符画大家一定非常熟悉了,那么如何把一张现有的图片转成字符画呢? HTML5 让这个可能变成了现实,通过 canvas,可以很轻松实现这个功能. 其实原理很简单:扫描图片相应位置的像素点,再计算出其灰 ...

  8. .Net的PDF转图片

    用的是破解版的 O2S.Components.PDFRender4NET.dll 插件, 简单引用即可 public static class PdfToImage { , , ) { try { / ...

  9. 将图片转为ASCII字符画

    原文:将图片转为ASCII字符画 Copyright 2012 Conmajia 源代码下载:点击这里 什么是字符画?就是用ASCII字符来近似组成图像,就像这样: ╭╮ ╭╮ ││ ││ ╭┴┴—— ...

随机推荐

  1. 浅谈 Xamarin Community Toolkit 的未来发展

    .NET MAUI会在今年晚些时候发布,我们也很高兴和大家一起分享我们对Xamarin Community Toolkit的计划! 这包括 .NET MAUI Community Toolkit.Xa ...

  2. visual studio下载速度为0解决方法

    步骤: 一,更改网络设置 二,cmd刷新dns 一,更改网络设置 1,点开控制面板,打开网络和Internet 2,点击网络和共享中心 3,点击你连接的网络,那个是你连接的WIFI名字 4,点击属性 ...

  3. 还不知道PHP有闭包?那你真OUT了

    做过一段时间的Web开发,我们都知道或者了解JavaScript中有个非常强大的语法,那就是闭包.其实,在PHP中也早就有了闭包函数的功能.早在5.3版本的PHP中,闭包函数就已经出现了.到了7以及后 ...

  4. Java基础系列(24)- 增强for循环

    增强for循环 这里我们先只是见一面,做个了解,之后数组部分会重点使用 Java5引入了一种主要用于数组或集合的增强型for循环 Java增强for循环语法格式如下 for(声明语句:表达式){ // ...

  5. Java基础系列(11)- 变量、常量、作用域以及变量的命名规范

    变量 变量是什么:就是可以变化的量 Java是一种强类型语言,每个变量都必须声明其类型 Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域 type varName [=valu ...

  6. 写SQL的套路

    定义问题 转化问题 如要解决的问题是:查出每门课程成绩都大于80分学生的姓名,可以转化为:只要学生最小分数的课程大于80分,就是所有课程成绩都大于80分. 查询同名同姓学生名单并统计同名人数--> ...

  7. P7514-[省选联考2021A/B卷]卡牌游戏【贪心】

    正题 题目链接:https://www.luogu.com.cn/problem/P7514 题目大意 给出\(n\)个卡牌有\(a_i/b_i\),开始都是\(a_i\)朝上,将不超过\(m\)张卡 ...

  8. CF643F-Bears and Juice【组合数学】

    正题 题目链接:https://www.luogu.com.cn/problem/CF643F 题目大意 题目有点奇怪就直接放翻译了 有 \(n\) 只熊和若干桶果汁和恰好一桶酒,每一天每只熊会选择一 ...

  9. AT4120-[ARC096D]Sweet Alchemy【贪心,背包】

    正题 题目链接:https://www.luogu.com.cn/problem/AT4120 题目大意 给出\(n\)个物品和一个容量\(m\),第\(i\)个物品体积为\(c_i\).除了第一个物 ...

  10. 使用jacob调用Windows的com对象,进行word、ppt等转换成ptf、html(二)

    富文本转pdf : 注意:simsun.ttc 可以百度下载:http://www.pc6.com/softview/SoftView_100415.html package com.orangecd ...