更高精度的pdf bookmark切片

举报
Amrf 发表于 2019/09/24 11:43:39 2019/09/24
【摘要】 https://github.com/mozilla/pdf.jspdf.js目录下执行npm install时获取prebuilt时由于一些网络问题获取不到时,本地编译node-canvas报错,看了一下node-canvas的windows依赖有https://github.com/Automattic/node-canvas/wiki/Installation:-Windows(gtk...

问题场景:

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

image.png

解决方案:

通过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);
    }
  • 切片逻辑

image.png


其他:

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

/*================================================================*/


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。