关于Java后台 HTML+CSS3 转换生成PDF文件问题求助!

关于Java后台 HTML+CSS3 转换生成PDF文件问题求助!

公司需求:HTML+CSS3(CSS样式是直接写在HTML文件当中)转换为PDF文件,并上传阿里云OSS供用户下载

  • 本身这个需求并不难,功能做完后不管是PDF质量还是生成速度都还不错,其中也遇到某些CSS样式不支持的问题,都用其他支持的样式替代

  • 接下来问题就出现了,文本中突然出现竖行文本CSS样式标签为:writing-mode,生成的PDF中文本并没有竖行显示,明显是itext解析时并不认识这个标签。

  • 由于业务逻辑要求,不可采用控制文本区宽度强制文字换行和其他物理方式来实现,目前查了大量资料,所有在HTML转换PDF这个问题上,对CSS都不太友好。

  • 后试过用Java调用虚拟打印机、先转word在转pdf、wkhtmltopdf、html2pdf等均没成功

  • 总结来说当html中出现竖行文本时,转换PDF希望所见即所得。

  • 现在求助有这方面经验的大神能给点解决思路,或者实现方式,小弟在此不胜感激,后面附上源码。

	<!-- Itext Html To PDF -->
	<dependency>  
		<groupId>org.freemarker</groupId>  
		<artifactId>freemarker</artifactId>  
		<version>2.3.23</version>  
	</dependency>  
	
	<dependency>
		<groupId>org.apache.pdfbox</groupId>
		<artifactId>pdfbox</artifactId>
		<version>2.0.2</version>
	</dependency>
	
	<dependency>
		<groupId>org.xhtmlrenderer</groupId>
		<artifactId>flying-saucer-pdf-itext5</artifactId>
		<version>9.1.6</version>
	</dependency>
	
	<dependency>
		<groupId>net.sf.jtidy</groupId>
		<artifactId>jtidy</artifactId>
		<version>r938</version>
	</dependency>
	
	<dependency>
		<groupId>com.aliyun.oss</groupId>
		<artifactId>aliyun-sdk-oss</artifactId>
		<version>2.7.0</version>
	</dependency>
	@Override
	public void createPDF(HttpServletRequest request,String bookId) {
		
		Book book = bookDao.get(bookId);
		List<Map<String,Object>> listProvider = bookHtmlDao.getProviderBy(bookId);
		bookEditionDao.updateEditionNo(bookId, "1");
		String providerName = listProvider.get(0).get("PROVIDER_NAME").toString();
		String providerId = listProvider.get(0).get("PROVIDER_ID").toString();
	
		// 初始化目录地址
		String ftlPath = request.getSession().getServletContext().getRealPath("/WEB-INF/classes/cn/jointat/mfb/service/toPDF");
		String ftlName = "hzSur";
		String outputPath = "D:/diskPDF/"+providerId+"_"+providerName+"/"+book.getBookName()+"/"+ftlName;
		
		// 有水印跟无水印版本分文件夹存
		String watermarkYesPath = outputPath+"/watermarkYes";
		String watermarkNoPath = outputPath+"/watermarkNo";
		
		System.out.println("====>服务商 【"+providerName+"】 请求生成PDF");
		System.out.println("========================= PDF开始生成 【"+book.getBookName()+"】 =========================");
		// 遍历Html代码块集合
		List<BookHtml> listBookHtml = bookHtmlDao.getBookHtml(bookId);
		List<String> filesInFolder = new ArrayList<String>();
		for (int i = 0; i < listBookHtml.size(); i++) {
			String htmlString = listBookHtml.get(i).getHtmlContent();		//书页html内容
			// 将图片oss地址转换为内网地址
	        	htmlString = htmlString.replace("oss-cn-hangzhou.aliyuncs.com", "oss-cn-hangzhou-internal.aliyuncs.com");
			String outputName = book.getBookName()+"_"+(i+1);
			// 组装待合并的PDF路径
			filesInFolder.add(outputName+".pdf");
			
			// 有水印版本
			if(!PDFUtil.createPDF(ftlPath, ftlName, watermarkYesPath, outputName, htmlString, true)) {
				System.out.println("====>出现异常,导致错误的原因可能是html源码格式错误或模板错误,请联系管理员。");
			}
			// 无水印版本
			if(!PDFUtil.createPDF(ftlPath, ftlName, watermarkNoPath, outputName, htmlString, false)) {
				System.out.println("====>出现异常,导致错误的原因可能是html源码格式错误或模板错误,请联系管理员。");
			}
			System.out.println("====>第 【"+(i+1)+"】 张生成完成");
			// 封面页PDF生成图片base64
			if(i==0) {
				String base64 = PDFUtil.transPDFBase64(watermarkNoPath,outputName, 20);
				book.setBookLogoImg(base64);
				bookDao.update(book);
			}
		}

		System.out.println("====>开始合并");
		PDFUtil.mergePDF(watermarkYesPath, filesInFolder, book.getBookName()+"_清样.pdf");
		PDFUtil.mergePDF(watermarkNoPath, filesInFolder, book.getBookName()+"_原稿.pdf");
		System.out.println("========================= PDF结束生成 【"+book.getBookName()+"】 =========================");
		
		// 得到本地合并后的pdf路径
		String editionDownloadUrlYes = watermarkYesPath+"/"+book.getBookName()+"_清样.pdf";
		String editionDownloadUrlNo = watermarkNoPath+"/"+book.getBookName()+"_原稿.pdf";
		
		// 清样上传到阿里云oss
		System.out.println("====>清样上传到阿里云oss");
		String keyYes = "generatePDF/"+providerId+"_"+providerName+"/"+ftlName+"/Watermarks/"+book.getBookName()+"_清样.pdf";
		File fileYes = new File(editionDownloadUrlYes);
		Map<String,Object> mapYes = OssUtil.uploadFileOss("jointat-generate", keyYes, fileYes);
		
		// 原稿上传到阿里云oss
		System.out.println("====>原稿上传到阿里云oss");
		String keyNo = "generatePDF/"+providerId+"_"+providerName+"/"+ftlName+"/Manuscript/"+book.getBookName()+"_原稿.pdf";
		File fileNo = new File(editionDownloadUrlNo);
		Map<String,Object> mapNo = OssUtil.uploadFileOss("jointat-generate", keyNo, fileNo);
		
		// 更新PDF为可下载状态、下载地址
		String linkYes = mapYes.get("link").toString();
		String linkNo = mapNo.get("link").toString();
		if(!linkYes.equals("false") && !linkNo.equals("false")) {
			System.out.println("====>更新谱书为可下载状态");
			bookEditionDao.updateEditionYes(bookId, "1", linkYes, linkNo);
		}
		
		// 删除文件夹
		PDFUtil.delAllFile(watermarkYesPath);
		PDFUtil.delAllFile(watermarkNoPath);
	}

	 * html生成pdf
	 * @param ftlPath 		ftl模板目录路径
	 * @param ftlName		ftl模板名称
	 * @param outputPath		pdf输出路径
	 * @param outputName		pdf输出名称
	 * @param htmlString		html源码
	 * @param watermark		是否添加水印【true-添加、false=不添加】
	 * @return
	public static boolean createPDF(String ftlPath,String ftlName,String outputPath,String outputName,String htmlString,boolean watermark) {

		FileOutputStream os = null;
		try {
			// 创建一个freemarker.template.Configuration实例,它是存储 FreeMarker 
			Configuration cf = new Configuration(Configuration.VERSION_2_3_22);  								// 指定版本号
	        	cf.setDirectoryForTemplateLoading(new File(ftlPath));  	   											// 设置模板目录 
	        	cf.setDefaultEncoding("UTF-8");  																	// 设置默认编码格式
	        	Template temp = cf.getTemplate(ftlName+".ftl"); 													// 从设置的目录中获得模板 
	      
	        	// html转换为xhtml
			ByteArrayInputStream inputStream = new ByteArrayInputStream(htmlString.getBytes("UTF-8"));
	        	ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
	        
		        // 使用jtidy解析
		        Tidy tidy = new Tidy();							// 实例化Tidy对象
		        tidy.setInputEncoding("UTF-8");						// 设置输入
		        tidy.setQuiet(false);							// 如果是true 不输出注释,警告和错误信息
		        tidy.setShowWarnings(false);						// 不显示警告信息
		        tidy.setIndentContent(false);						// 缩进适当的标签内容。
		        tidy.setSmartIndent(false);						// 内容缩进
		        tidy.setIndentAttributes(false);
		        tidy.setPrintBodyOnly(true);						// 只输出body内部的内容
		        tidy.setWraplen(1024);							// 多长换行
		        tidy.setXHTML(true);							// 输出为xhtml
		        tidy.setTrimEmptyElements(false);               			// 不输出空元素
		        tidy.setMakeClean(false);						// 去掉没用的标签
		        tidy.setWord2000(false);						// 清洗word2000的内容
		        tidy.setErrout(new PrintWriter(System.out));				// 设置错误输出信息
		        tidy.parse(inputStream, outputStream);

			// 从模板生成html文件
			String fileHtml = "hzSur.html";
			File file = new File(fileHtml);
		        if (!file.exists()) {  
		            file.createNewFile();  
		        }  
	        
		        // 是否构造水印模板
		        String htmlWatermark = "";
		        if(watermark) {
		        	htmlWatermark = "<div class='outFlows'>";
			        for (int i = 0; i < 10; i++) {
			        	htmlWatermark += "<div></div>";
					}
			        htmlWatermark +="</div>";
		        }
		        
		        // 将数据写入模板
		        Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"UTF-8"));  
		        Map<String, Object> product = new HashMap<String, Object>();  
		        product.put("htmlWatermark", htmlWatermark);
		        product.put("htmlString", outputStream.toString());
		        temp.process(product, out);  
		        out.close();  
		  
		        // 定义输出目录路径及文件名称
		        File outDir =new File(outputPath);
		        outDir.mkdirs();
		        ITextRenderer renderer = new ITextRenderer();  
	 	        os = new FileOutputStream(outputPath+"/"+outputName+".pdf");  
	 	        renderer.setDocument(new File(fileHtml).toURI().toURL().toString());  
	        
		        // 获取中文字体  
		        ITextFontResolver fontResolver = renderer.getFontResolver();  
	        	fontResolver.addFont(ftlPath+"/MSYH.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			fontResolver.addFont(ftlPath+"/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);	        
			fontResolver.addFont(ftlPath+"/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			fontResolver.addFont(ftlPath+"/STKAITI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			fontResolver.addFont(ftlPath+"/SIMLI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			fontResolver.addFont(ftlPath+"/simfang.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			fontResolver.addFont(ftlPath+"/FZSTK.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			fontResolver.addFont(ftlPath+"/FZYTK.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			fontResolver.addFont(ftlPath+"/simhei.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
			fontResolver.addFont(ftlPath+"/SIMYOU.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

			// 输出
		        renderer.layout();  
		        renderer.createPDF(os);  
		        renderer.finishPDF();  
		        renderer = null;  
		        os.close();
		        return true;
		} catch (Exception e) {
			System.out.println(e);
			e.printStackTrace();
			return false;
		}
	}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值