在项目中,利用iText和flying saucer生成pdf文件,网上所说的中文不显示的问题倒是没有遇到,不过就是中文字体时,由于字符宽度是按字母计算的,同样字数会导致一行显示很长不换行,从而超过版面宽度显示不全的问题。经过分析和查找,终于的到解决方案,另外也解决了中文标点符号出现在行首的现象。
首先,我们要明白这几个东西的真实含义:
Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS : 4E00-9FBF:CJK 统一表意符号
Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS :F900-FAFF:CJK 兼容象形文字Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A :3400-4DBF:CJK 统一表意符号扩展 A
CJK的意思是“Chinese,Japanese,Korea”的简写 ,实际上就是指中日韩三国的象形文字的Unicode编码
Character.UnicodeBlock.GENERAL_PUNCTUATION :2000-206F:常用标点Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION :3000-303F:CJK 符号和标点Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS :FF00-FFEF:半角及全角形式
在flying saucer生成pdf的过程中,源代码中是按组和行宽来换行的,具体逻辑如下:
以空格为分组符,任意两个相邻空格之间的字符都是不可换行的,当该行多组字符的总长到达最接近行宽的时候,也就是再加一个字符组就会超过行宽的时候,就会换行
这种方式对英文是很凑效的,但是对中文是不行的,因为中文不是以空格分隔的。yye_javaeye老兄对源码做了改进,判断是否是中文字符,如果是中文字符,则把每个中文字符做一组,然后再用组+行宽来换行。他添加了两个方法:isChinese(char c)和getStrRight(String s,int left), 改过后的部分源码如下:
package org.xhtmlrenderer.layout; import org.xhtmlrenderer.css.constants.IdentValue; import org.xhtmlrenderer.css.style.CalculatedStyle; import org.xhtmlrenderer.render.FSFont; /** * A utility class that scans the text of a single inline box, looking for the * next break point. * @author Torbjrn Gannholm */ public class Breaker { 。。。。。。 public static void breakText(LayoutContext c, LineBreakContext context, int avail, CalculatedStyle style) { 。。。。。。 String currentString = context.getStartSubstring(); int left = 0; // int right = currentString.indexOf(WhitespaceStripper.SPACE, left + 1); int right = getStrRight(currentString,left); int lastWrap = 0; int graphicsLength = 0; int lastGraphicsLength = 0; while (right > 0 && graphicsLength <= avail) { lastGraphicsLength = graphicsLength; graphicsLength += c.getTextRenderer().getWidth( c.getFontContext(), font, currentString.substring(left, right)); lastWrap = left; left = right; // right = currentString.indexOf(WhitespaceStripper.SPACE, left + 1); right = getStrRight(currentString,left+1); } 。。。。。。 } private static boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { return true; } return false; } private static int getStrRight(String s,int left){ if(left>=s.length()) return -1; char[] ch = s.toCharArray(); for(int i = left;i<ch.length;i++){ if(isChinese(ch[i]) || ' ' == ch[i]){ return i==0?i+1:i; } } return -1; } }
这种方法确实实现了中文汉字的换行,但是也带来了“标点符号在行首”的问题,因为他把标点符号也视作汉字了,这样标点符号也被分组被换行了,解决的方法就是把标点符号从isChinese方法中去掉。其源码如下:
/* * Breaker.java * Copyright (c) 2004, 2005 Torbj锟絩n Gannholm, * Copyright (c) 2005 Wisconsin Court System * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.xhtmlrenderer.layout; import org.xhtmlrenderer.css.constants.IdentValue; import org.xhtmlrenderer.css.style.CalculatedStyle; import org.xhtmlrenderer.render.FSFont; /** * A utility class that scans the text of a single inline box, looking for the * next break point. * @author Torbj锟絩n Gannholm */ public class Breaker { public static void breakFirstLetter(LayoutContext c, LineBreakContext context, int avail, CalculatedStyle style) { FSFont font = style.getFSFont(c); context.setEnd(getFirstLetterEnd(context.getMaster(), context.getStart())); context.setWidth(c.getTextRenderer().getWidth( c.getFontContext(), font, context.getCalculatedSubstring())); if (context.getWidth() > avail) { context.setNeedsNewLine(true); context.setUnbreakable(true); } } private static int getFirstLetterEnd(String text, int start) { int i = start; while (i < text.length()) { char c = text.charAt(i); int type = Character.getType(c); if (type == Character.START_PUNCTUATION || type == Character.END_PUNCTUATION || type == Character.INITIAL_QUOTE_PUNCTUATION || type == Character.FINAL_QUOTE_PUNCTUATION || type == Character.OTHER_PUNCTUATION) { i++; } else { break; } } if (i < text.length()) { i++; } return i; } public static void breakText(LayoutContext c, LineBreakContext context, int avail, CalculatedStyle style) { FSFont font = style.getFSFont(c); IdentValue whitespace = style.getWhitespace(); // ====== handle nowrap if (whitespace == IdentValue.NOWRAP) { context.setEnd(context.getLast()); context.setWidth(c.getTextRenderer().getWidth( c.getFontContext(), font, context.getCalculatedSubstring())); return; } //check if we should break on the next newline if (whitespace == IdentValue.PRE || whitespace == IdentValue.PRE_WRAP || whitespace == IdentValue.PRE_LINE) { int n = context.getStartSubstring().indexOf(WhitespaceStripper.EOL); if (n > -1) { context.setEnd(context.getStart() + n + 1); context.setWidth(c.getTextRenderer().getWidth( c.getFontContext(), font, context.getCalculatedSubstring())); context.setNeedsNewLine(true); context.setEndsOnNL(true); } else if (whitespace == IdentValue.PRE) { context.setEnd(context.getLast()); context.setWidth(c.getTextRenderer().getWidth( c.getFontContext(), font, context.getCalculatedSubstring())); } } //check if we may wrap if (whitespace == IdentValue.PRE || (context.isNeedsNewLine() && context.getWidth() <= avail)) { return; } context.setEndsOnNL(false); String currentString = context.getStartSubstring(); int left = 0; // int right = currentString.indexOf(WhitespaceStripper.SPACE, left + 1); int right = getStrRight(currentString,left); int lastWrap = 0; int graphicsLength = 0; int lastGraphicsLength = 0; while (right > 0 && graphicsLength <= avail) { lastGraphicsLength = graphicsLength; graphicsLength += c.getTextRenderer().getWidth( c.getFontContext(), font, currentString.substring(left, right)); lastWrap = left; left = right; // right = currentString.indexOf(WhitespaceStripper.SPACE, left + 1); right = getStrRight(currentString,left+1); } if (graphicsLength <= avail) { //try for the last bit too! lastWrap = left; lastGraphicsLength = graphicsLength; graphicsLength += c.getTextRenderer().getWidth( c.getFontContext(), font, currentString.substring(left)); } if (graphicsLength <= avail) { context.setWidth(graphicsLength); context.setEnd(context.getMaster().length()); //It fit! return; } context.setNeedsNewLine(true); if (lastWrap != 0) {//found a place to wrap context.setEnd(context.getStart() + lastWrap); context.setWidth(lastGraphicsLength); } else {//unbreakable string if (left == 0) { left = currentString.length(); } context.setEnd(context.getStart() + left); context.setUnbreakable(true); if (left == currentString.length()) { context.setWidth(c.getTextRenderer().getWidth( c.getFontContext(), font, context.getCalculatedSubstring())); } else { context.setWidth(graphicsLength); } } return; } private static boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A) { return true; } return false; } private static int getStrRight(String s,int left){ if(left>=s.length()) return -1; char[] ch = s.toCharArray(); for(int i = left;i<ch.length;i++){ if(isChinese(ch[i]) || ' ' == ch[i]){ return i==0?i+1:i; } } return -1; } }
由于我使用的是maven管理项目依赖,包名有所变化
依赖:
<dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf-itext5</artifactId> <version>9.0.1</version> </dependency>
上面会引入以下几个依赖项:
flying-saucer-parent-9.0.1.pom
flying-saucer-pdf-itext5-9.0.1.jar
flying-saucer-core-9.0.1.jar
core-renderer-R8.jar
上面对应的代码就在flying-saucer-core-9.0.1.jar包中,修改之即可。
另外需要注意的就是,目前flying-saucer支持的最高版本是CSS 2.1,如果用了CSS 3的内容,可能会不起作用而导致版面错乱。
http://bettereveryday.iteye.com/blog/611561
相关推荐
在使用flying-saucer生成PDF时,可能会遇到图片无法正确显示的情况。这通常与图片的路径、格式支持以及资源加载有关。确保图片URL是相对或绝对的正确路径,并且flying-saucer支持的图片格式(如JPEG、PNG、GIF)。...
`flying-saucer-pdf`支持嵌入字体,可以在转换时指定包含中文字符的字体文件,如Arial Unicode MS或SimSun。 2. 在HTML或CSS中设置字体-family属性,确保优先使用支持中文的字体。 3. 如果是服务器端转换,确保...
本实例将深入探讨如何利用Flying Saucer生成PDF,并解决其中遇到的中文显示问题。 首先,Flying Saucer基于IText库,IText是一款强大的PDF处理工具,它提供了丰富的API用于创建、修改和处理PDF文档。Flying Saucer...
FlyingSaucer支持中文字符的正确显示,且可以正确处理CSS样式和图片。 三、SpringBoot项目搭建 为了实现PDF在线预览功能,我们需要先搭建一个SpringBoot项目。首先,需要在pom.xml文件中添加相应的依赖项,包括...
`Flyingsaucer`是一个开源Java库,专门用于将HTML内容转换为PDF或其他格式,它支持CSS2.1标准,使得网页的布局和样式能够很好地在PDF中保留。在处理中文内容时,`Flyingsaucer`可能面临一些挑战,但通过正确的配置,...
当我们遇到"Flying Saucer的中文不显示问题"时,这通常是由于字符编码设置不正确或者字体支持不足所导致的。 在默认情况下,Flying Saucer可能不包含对中文字符集的支持,因此在尝试转换包含中文的网页时,可能会...
为了解决"Java使用Itext生成PDF中文不换行"的问题,我们可以采取以下几种策略: 1. **设置字体和编码**:确保使用支持中文的字体,如SimSun、Arial Unicode MS等,并正确设置PDF的编码为UTF-8。Itext中的`Font`类...
基于iText和flying saucer结合freemark生成pdf 范例 1. 使用maven构建,不含jar包,可以自行使用maven下载依赖包, 2. 使用前需要将C:/Windows/Fonts/ARIALUNI.TTF 复制到doc-render/src/test/resources/config/fonts...
Flying Saucer生成pdf文档jar包 ,已做修改,支持中文 换行,但是 table标签换行 请改用CSS 样式:style="table-layout:fixed; word-break:break-strict;
iText和Flying Saucer是两个非常流行的Java库,用于生成高质量的PDF文档。这两个库结合使用,可以有效地处理HTML内容并将其转换为PDF格式,同时支持中文字符集,解决了在生成PDF时中文显示不正常的问题。 首先,...
在与flying saucer结合使用时,iText通常用来完成最后的PDF生成步骤,将flying saucer处理后的页面流转换为完整的PDF文档。 【freemarker】是一个模板引擎,常用于生成HTML、XML或其他文本格式的文档。开发者可以...
总的来说,iText+Flying Saucer生成PDF文档时遇到的中文不显示和不自动换行问题,可以通过调整字体设置、优化CSS布局和正确使用PageBreaker类来解决。在实际开发中,要充分理解这两个库的工作原理,结合实际需求进行...
在`flying-saucer`中,iText作为底层库用于实际的PDF生成,处理页面布局、文本流、图像插入等任务。版本2.1.7虽然较旧,但依然稳定且功能完善。 使用这些库进行HTML到PDF的转换通常涉及以下步骤: - 创建一个`...
flying-saucer-coreR-9.0.7 中修改Breaker类,解决PDF中文没有对齐问题。
Java JSP 生成 PDF 使用的是一个名为 Flying Saucer 的库,它是一个开源项目,专门用于将 HTML 内容转换为高质量的 PDF 文件。这个过程在 IT 行业中非常重要,因为许多应用程序需要将动态生成的网页内容导出为可打印...
- **PDF文件生成**: FlyingSaucer支持将文档输出为PDF格式。 - **字体控制**: 用户可以根据需要添加自定义字体或指定特定编码下的字体。 - **页面尺寸设置**: 可以自由控制生成PDF文档的页面大小。 - **页边距调整**...
Flying Saucer 支持中文宋体win,Linux; Css 页面字体修改为 font-family:STSong; 就可以了,具体
总结,Java提供了一系列工具和库,如Apache PDFBox、iText、Flying Saucer等,用于根据模板动态生成PDF文件。开发者可以根据实际需求和项目规模选择合适的库,并结合模板引擎实现高效的数据绑定和文档生成。在实际...
基于iText 和 flying saucer结合freemark java 生成 pdf