更高精度的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)