`

java flying saucer生成的pdf文件中文、样式、换行问题

 
阅读更多

在项目中,利用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

    在使用flying-saucer生成PDF时,可能会遇到图片无法正确显示的情况。这通常与图片的路径、格式支持以及资源加载有关。确保图片URL是相对或绝对的正确路径,并且flying-saucer支持的图片格式(如JPEG、PNG、GIF)。...

    flying-saucer-pdf 生成pdf解决图片问题 解决中文问题

    `flying-saucer-pdf`支持嵌入字体,可以在转换时指定包含中文字符的字体文件,如Arial Unicode MS或SimSun。 2. 在HTML或CSS中设置字体-family属性,确保优先使用支持中文的字体。 3. 如果是服务器端转换,确保...

    利用flying saucer 生成Pdf的例子

    本实例将深入探讨如何利用Flying Saucer生成PDF,并解决其中遇到的中文显示问题。 首先,Flying Saucer基于IText库,IText是一款强大的PDF处理工具,它提供了丰富的API用于创建、修改和处理PDF文档。Flying Saucer...

    SpringBoot集成Freemarker+FlyingSaucer实现pdf在线预览.pdf

    FlyingSaucer支持中文字符的正确显示,且可以正确处理CSS样式和图片。 三、SpringBoot项目搭建 为了实现PDF在线预览功能,我们需要先搭建一个SpringBoot项目。首先,需要在pom.xml文件中添加相应的依赖项,包括...

    flyingsaucer转html为PDF(中文可用)

    `Flyingsaucer`是一个开源Java库,专门用于将HTML内容转换为PDF或其他格式,它支持CSS2.1标准,使得网页的布局和样式能够很好地在PDF中保留。在处理中文内容时,`Flyingsaucer`可能面临一些挑战,但通过正确的配置,...

    flying saucer的中文不显示问题(修改源码后重新打的jar包)

    当我们遇到"Flying Saucer的中文不显示问题"时,这通常是由于字符编码设置不正确或者字体支持不足所导致的。 在默认情况下,Flying Saucer可能不包含对中文字符集的支持,因此在尝试转换包含中文的网页时,可能会...

    解决Itext生成PDF中文不换行的jar

    为了解决"Java使用Itext生成PDF中文不换行"的问题,我们可以采取以下几种策略: 1. **设置字体和编码**:确保使用支持中文的字体,如SimSun、Arial Unicode MS等,并正确设置PDF的编码为UTF-8。Itext中的`Font`类...

    基于iText和flying saucer结合freemark生成pdf 范例

    基于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包

    Flying Saucer生成pdf文档jar包 ,已做修改,支持中文 换行,但是 table标签换行 请改用CSS 样式:style="table-layout:fixed; word-break:break-strict;

    iText+Flying Saucer生成pdf文档所需要的jar包

    iText和Flying Saucer是两个非常流行的Java库,用于生成高质量的PDF文档。这两个库结合使用,可以有效地处理HTML内容并将其转换为PDF格式,同时支持中文字符集,解决了在生成PDF时中文显示不正常的问题。 首先,...

    用flying saucer将html生成pdf的例子

    在与flying saucer结合使用时,iText通常用来完成最后的PDF生成步骤,将flying saucer处理后的页面流转换为完整的PDF文档。 【freemarker】是一个模板引擎,常用于生成HTML、XML或其他文本格式的文档。开发者可以...

    iText+Flying Saucer生成pdf文档,中文不显示和不自动换行问题重新Breaker.class

    总的来说,iText+Flying Saucer生成PDF文档时遇到的中文不显示和不自动换行问题,可以通过调整字体设置、优化CSS布局和正确使用PageBreaker类来解决。在实际开发中,要充分理解这两个库的工作原理,结合实际需求进行...

    flying-saucer-pdf-9.0.9转pdf相关包

    在`flying-saucer`中,iText作为底层库用于实际的PDF生成,处理页面布局、文本流、图像插入等任务。版本2.1.7虽然较旧,但依然稳定且功能完善。 使用这些库进行HTML到PDF的转换通常涉及以下步骤: - 创建一个`...

    flying-saucer-coreR 解决PDF中文没对齐问题

    flying-saucer-coreR-9.0.7 中修改Breaker类,解决PDF中文没有对齐问题。

    java jsp 生成 pdf flyingsaucer

    Java JSP 生成 PDF 使用的是一个名为 Flying Saucer 的库,它是一个开源项目,专门用于将 HTML 内容转换为高质量的 PDF 文件。这个过程在 IT 行业中非常重要,因为许多应用程序需要将动态生成的网页内容导出为可打印...

    flyingsaucer指导文档

    - **PDF文件生成**: FlyingSaucer支持将文档输出为PDF格式。 - **字体控制**: 用户可以根据需要添加自定义字体或指定特定编码下的字体。 - **页面尺寸设置**: 可以自由控制生成PDF文档的页面大小。 - **页边距调整**...

    Flying Saucer 支持中文宋体win,Linux,直接使用

    Flying Saucer 支持中文宋体win,Linux; Css 页面字体修改为 font-family:STSong; 就可以了,具体

    基于java根据模板动态生成PDF文件

    总结,Java提供了一系列工具和库,如Apache PDFBox、iText、Flying Saucer等,用于根据模板动态生成PDF文件。开发者可以根据实际需求和项目规模选择合适的库,并结合模板引擎实现高效的数据绑定和文档生成。在实际...

    flying-saucer-pdf-9.0.3.jar

    基于iText 和 flying saucer结合freemark java 生成 pdf

Global site tag (gtag.js) - Google Analytics