更高精度的pdf bookmark切片
问题场景:
用户输入一些关键词,如果匹配到pdf的某个bookmark则返回该段bookmark指向的具体内容,(pdf文件数量会有多个),需要匹配的bookmark基础条件的其中包含规则和建议,bookmark结构如下:

解决方案:
通过aspose可以将获取bookmark指向的页,单页转成html或者png图片很简单,但是这样转换的进度就只到页,效果不是太好,查询了一些其他方案,有很多的精度也只是到页的切片;
中途考虑过采用前端方案,采用pdf.js在view后面加上参数直接前端跳转到指定的bookmark处,但是有个问题是pdf整个文件很大的时候加载会比较久,尤其是多pdf的时候,效果还是不理想;
后来看到aspose.pdf页面cropbox方法,可以实现截取一个页面中的一块区域转换成图片的功能,可以达到不错的效果,但是当bookmark指向的内容跨页的时候,就需要将上下两部分的截图拼到一起,为了达到更好的效果,还需要将这种情况下的图片crop掉上下的空白区域;(缺陷是当bookmark的相邻的下一个bookmark在下下页或者间隔更多页的时候,中间页没有处理)
public static BufferedImage crop(BufferedImage image) {
int minY = 0, maxY = 0, minX = Integer.MAX_VALUE, maxX = 0;
boolean isBlank, minYIsDefined = false;
Raster raster = image.getRaster();
for (int y = 0; y < image.getHeight(); y++) {
isBlank = true;
for (int x = 0; x < image.getWidth(); x++) {
//Change condition to (raster.getSample(x, y, 3) != 0)
//for better performance
if (raster.getSample(x, y, 0) != 255 || raster.getSample(x, y, 1) != 255 || raster.getSample(x, y, 2) != 255) {//raster.getPixel(x, y, (int[]) null)[3] != 0
isBlank = false;
if (x < minX) minX = x;
if (x > maxX) maxX = x;
}
}
if (!isBlank) {
if (!minYIsDefined) {
minY = y;
minYIsDefined = true;
} else {
if (y > maxY) maxY = y;
}
}
}
return image.getSubimage(0, minY, image.getWidth(), maxY - minY + 1);
//return image.getSubimage(minX, minY, maxX - minX + 1, maxY - minY + 1);
}切片逻辑

其他:
pdf.js使用过程中的一些问题记录
https://github.com/mozilla/pdf.js pdf.js目录下执行npm install时获取prebuilt时由于一些网络问题获取不到时, 本地编译node-canvas报错,看了一下node-canvas的windows依赖有 https://github.com/Automattic/node-canvas/wiki/Installation:-Windows (gtk2的一个镜像链接-http://ftp.vim.org/pub/windowing/gnome/binaries/win64/gtk+/2.22/, libjpeg-turbo镜像链接 -http://mirrors.huaweicloud.com/repository/toolkit/libjpeg-turbo/2.0.0/) 需要注意的是gtk2选择win64,libjpeg也要选择win64版本,否则还会出现链接错误; npm install执行成功后使用gulp server验证ok
aspose.pdf使用限制解除过程中的一些问题记录(支持付费,测试和学习除外)
首先要找到合适的证书文件(https://github.com/fanjingdan012/JavaDetails/blob/8df20f1f20c7345a0876776276af8245a9ee3fe4/pdf/src/test/resources/licence/Aspose.Pdf.162356.lic),否则会出现没有任何报错但是处理出来的文件含有eval标记并且只能处理一部分
出现证书过期错误后就可以定位到报错位置,不同版本代码混淆后的类名可能不一样,类中存在类名为1的内部类改名处理掉,
处理掉其他语法错误,注释掉报错的异常点就可以正常使用了;
/*================================================================*/
aspose.cells解除水印:
参考:
https://onew.me/tags/aspose-crack/+&cd=10&hl=en&ct=clnk&gl=sg
关键是替换函数体
this.a = new com.aspose.cells.License(); com.aspose.cells.XXXX.a();
其他的异常注释就好;
/*================================================================*/
aspose.cells 表格转网页修改附属文件目录,并且在所有附属文件前面加前缀,并且文件中所有href中添加http:xxx域
可以使用options.setAttachedFilesDirectory("D:/xxxx");===>注意只有输出为流时有效
但是还有问题, 这样修改后输出的主文件中的href中会包含D:/xxxx,
设置options.setAttachedFilesUrlPrefix("http://www.example.com/report/")
其他的sheet文件对应的.htm中href的前缀只有部分变成预期,主文件中则完全不变;
---------------
HtmlSaveOptions options = new HtmlSaveOptions();
options.setExportImagesAsBase64(true);
options.setCreateDirectory(true);
options.setStreamProvider(new IStreamProvider() {
private Map<OutputStream,String> oPairs = new HashMap<>();
@Override
public void initStream(StreamProviderOptions providerOptions) throws Exception {
String tName = providerOptions.getDefaultPath();
if(providerOptions.getDefaultPath().startsWith("/")) {
tName = tName.replace("/", "/"+prefix+"_");
}else if(providerOptions.getDefaultPath().startsWith("\\")) {
tName = tName.replace("\\", "\\"+prefix+"_");
}
String customPath = "D:/xxxx" + tName.replace("null", "");
File customFile = new File(customPath);
if (!customFile.getParentFile().exists()) {
customFile.getParentFile().mkdirs();
}
providerOptions.setCustomPath(customFile.getCanonicalPath());
OutputStream fs = new FileOutputStream(customFile);
providerOptions.setStream(fs);
oPairs.put(fs,customFile.getCanonicalPath());
ret.add(customFile.getCanonicalPath());
}
@Override
public void closeStream(StreamProviderOptions providerOptions) throws Exception {
OutputStream outputStream = null;
String tName = "";
try {
outputStream = providerOptions.getStream();
tName = oPairs.get(outputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
outputStream.close();
}
}
if(!tName.isEmpty()) {
if(tName.endsWith("htm")) {
String content = IOUtils.toString(new FileInputStream(tName),StandardCharsets.UTF_8);
content = content.replaceAll("href=\"([^\"]+\\.)(htm|css)\"", "href=\"http://xxxx/"+prefix+"_$1$2\"");
IOUtils.write(content, new FileOutputStream(tName), StandardCharsets.UTF_8);
if(!tName.contains("tabstrip")) {
Integer index = Integer.parseInt(tName.replaceAll(".*([\\d]+)\\.htm","$1"));
for(Integer key:sheetNamePairs.keySet()) {
try {
if(index == key) {
...
break;
}
}catch(Exception e) {
logger.info(e.getMessage());
}
}
}
}
}
}
});注意上面的处理没有包含最外层的保存成.html的文件的内容二次处理,所有在执行保存之后,还需要将这个输出的外层.html文件中的D:/xxx替换成http://yyy;
/*================================================================*/
- 点赞
- 收藏
- 关注作者
评论(0)