<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Shao Fan

    關(guān)于JAVA與軟件工程
    posts - 31, comments - 71, trackbacks - 0, articles - 4
      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    轉(zhuǎn)換HTML內(nèi)容為PDF格式(1)

    Posted on 2006-05-30 06:15 shaofan 閱讀(5684) 評(píng)論(7)  編輯  收藏 所屬分類: Java
    作者:Nick Afshartous

    英文原文:http://www.javaworld.com/javaworld/jw-04-2006/jw-0410-html.html

    翻譯:http://shaofan.blogjava.net

    把網(wǎng)頁內(nèi)容以PDF的格式呈獻(xiàn)有利于內(nèi)容的傳播。在一些應(yīng)用中,提供格式便于打印的文檔是一個(gè)必需的功能,比如員工利益表等。事實(shí)上,法律規(guī)定Summmary Plan Descriptions(SPDs)必須能夠打印,即使它們是在線提供的也是如此。然而只打印網(wǎng)頁本身是不夠的,因?yàn)榇蛴「袷奖匕砀駜?nèi)容和頁碼。

    ?

    為了提供這樣的功能,開發(fā)人員可以把HTML內(nèi)容轉(zhuǎn)換為PDF格式。在此即做介紹。這里介紹的這種方法只使用開源組件。一些商業(yè)產(chǎn)品也支持動(dòng)態(tài)的文檔生成,比如說Adobe,它有Document Server產(chǎn)品線。但是,使用商業(yè)產(chǎn)品的開銷是相當(dāng)可觀的。使用開源方案可以緩解開銷的問題,并增加了組件源碼的透明度。

    ?

    轉(zhuǎn)換過程包含以下三步:

    1.HTML轉(zhuǎn)換為XHTML;

    2.XHTML轉(zhuǎn)換為XSL-FO(Extensible Stylesheet Language Formatting Objects擴(kuò)展樣式表語言格式化對(duì)象)。這里使用XSL樣式表和XSLT轉(zhuǎn)換器;

    3.XSL-FO文檔傳遞給格式化程序來生成目標(biāo)PDF文檔。

    ?

    本文先介紹怎樣用命令行界面來做這種轉(zhuǎn)換,然后介紹怎樣在JAVA中使用DOM接口來做同樣的工作。

    ?

    組件版本:

    本文中的代碼在以下版本中進(jìn)行了測(cè)試:

    組件???? 版本

    JDK ????1.5_06

    JTidy ???r7-dev

    Xalan-J ?2.7

    FOP ????0.20.5

    ?

    使用命令行界面

    ?

    在轉(zhuǎn)換過程中的每一步都包含了從一個(gè)輸入文件生成輸出文件的過程。這個(gè)過程可以用下圖來表示:



    ?

    使用這三個(gè)工具的命令行界面開始我們的工作是個(gè)好方法,盡管這種方法并不適合產(chǎn)品級(jí)的系統(tǒng),因?yàn)樗枰疟P中寫入臨時(shí)的中間文件。這種額外的I/O會(huì)導(dǎo)致性能的降低。稍后,在我們用JAVA來調(diào)用這三個(gè)工具時(shí),這個(gè)問題就會(huì)得到解決。

    ?

    第一步:轉(zhuǎn)換HTMLXHTML

    ?

    第一步就是把HTML轉(zhuǎn)換為一個(gè)新的XHTML文件。當(dāng)然,如果文件本來已經(jīng)就是XHTML,那就不需要這一步了。

    ?

    我用JTidy來完成這個(gè)轉(zhuǎn)換。JTidyTidy HTML解析器的JAVA版本。在轉(zhuǎn)換的過程中,JTidy會(huì)自動(dòng)添加缺少的標(biāo)簽來創(chuàng)建格式良好(well-formed)XML文檔。我用的是在SourceForge上的最新版本r7-dev

    ?

    可以用以下的腳本來運(yùn)行JTidy

    #/bin/sh

    java -classpath lib/Tidy.jar org.w3c.tidy.Tidy -asxml $1 >$2

    ?

    此腳本設(shè)置了CLASSPATH并調(diào)用了JTidy。運(yùn)行時(shí),要輸入的文件是以命令行參數(shù)的形式傳給JTidy。默認(rèn)情況下,生成的XHTML將被輸出到標(biāo)準(zhǔn)輸出設(shè)備。-modify開關(guān)可以用來覆寫輸入文件。-asxml開關(guān)把JTidy的輸出重定向到格式良好的XML

    ?

    調(diào)用時(shí)像這樣:

    tidy.sh hello.html hello.xml

    ?

    hello.html(輸入)hello.xml(輸出)的內(nèi)容如下:

    ?

    <html>

    <head>

    ? <title>Hello World

    </head>

    <body>

    ?? <p> Hello World!

    </body>

    </html>

    ?

    ?

    <!DOCTYPE html PUBLIC quot;-//W3C//DTD XHTML 1.0 Strict//EN"

    ??? quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <html xmlns=quot;http://www.w3.org/1999/xhtml">

    <head>

    <meta name=quot;generator" content="HTML Tidy, see www.w3.org" />

    <title>Hello World</title>

    </head>

    <body>

    <p>Hello World!</p>

    </body>

    </html>

    ?

    要注意的是,在XML文件中的那個(gè)</p></title>JTidy自動(dòng)添加的[譯注1]

    ?

    ?

    第二步:轉(zhuǎn)換XHTMLXSL-FO[譯注2]

    ?

    下面,XHTML將被轉(zhuǎn)換為XSL-FO,一種用來為XML文檔指定打印格式的語言。我通過用XSLT轉(zhuǎn)換器(Apache Xalan)處理XSL樣式表來完成這個(gè)轉(zhuǎn)換。我使用的樣式表是由Antenna House提供的xhtml2fo.xslAntenna House是一個(gè)出售XSL-FO上商用格式程序的公司。

    ?

    xhtml2fo.xsl樣式表指定了如何把每個(gè)HTML標(biāo)簽翻譯成相應(yīng)的XSL-FO格式化命令序列。舉例來說,HTML中的H2標(biāo)簽在翻譯中被定義為:

    ?

    ??? <xsl:template match="html:h2">

    ????? <fo:block xsl:use-attribute-sets="h2">

    ??????? <xsl:call-template name="process-common-attributes-and-children"/>

    ????? </fo:block>

    ??? </xsl:template>

    ?

    在處理的過程中,每次遇到H2標(biāo)簽,以上XSLT模板都會(huì)被調(diào)用。html:前綴表明H2標(biāo)簽是HTML的命名空間(namespace)。樣式表的命名空間在頂層xsl:stylesheet指示符的屬性中被指定。在xhtml2fo.xsl的最頂層,我們可以看到它指定了三個(gè)命名空間,分別對(duì)應(yīng)于XSL,XSL-FOHTML語言。

    ?

    ??? <xsl:stylesheet version="1.0"

    ??????????????????? xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

    ??? ????????????????xmlns:fo="http://www.w3.org/1999/XSL/Format"

    ??????????????????? xmlns:html="http://www.w3.org/1999/xhtml">...

    ?

    模板中的第二行

    ?

    ??? <fo:block xsl:use-attribute-sets="h2">

    ?

    致使fo:block標(biāo)簽被輸出,并且H2的屬性被生成為fo:block標(biāo)簽的屬性和值。每個(gè)XSL-FO(block)都是一段文字,它們的格式基于塊的屬性的值。

    ?

    H2的屬性在樣式表中被定義為:

    ?

    ??? <xsl:attribute-set name="h2">

    ??????? <xsl:attribute name="start-indent">10mm

    ??????? <xsl:attribute name="end-indent">10mm

    ??????? <xsl:attribute name="space-before">1em

    ??????? <xsl:attribute name="space-after">0.5em

    ??????? <xsl:attribute name="font-size">x-large

    ??????? <xsl:attribute name="font-weight">bold

    ??????? <xsl:attribute name="color">black

    ?? </xsl:attribute-set>

    ?

    start-indent及其后的屬性用來指定H2塊的格式化后的外觀。當(dāng)你想改變PDF文檔中用同樣HTML標(biāo)簽的文字塊的外觀時(shí),使用屬性集可以使這種改變更加容易。只要改動(dòng)屬性的設(shè)置,那么輸出的文件中所有使用這些屬性的地方都會(huì)被改動(dòng)。

    ?

    下一個(gè)指示符調(diào)用一個(gè)名為"process-common-attributes-and-children"的模板:

    ?

    ??? <xsl:call-template name="process-common-attributes-and-children"/>

    ?

    這個(gè)模板在樣式表中被指定。它的作用是檢查一些普通的HTML屬性(lang,id,align,valign,style)并生成相應(yīng)的XSL-FO指示符。要觸發(fā)對(duì)嵌在頂層H2標(biāo)簽中的任意標(biāo)簽的翻譯,process-common-attributes-and-children會(huì)調(diào)用:

    ?

    ??? <xsl:apply-templates/>

    ?

    因此,如果輸入是

    ?

    ??? <h2> Hello <em> there </em> </h2>

    ?

    那么在H2的模板中的<xsl:apply-templates/>就會(huì)觸發(fā)用來翻譯<em>標(biāo)簽的模板。

    ?

    翻譯H2標(biāo)簽的輸出是:

    ?

    ??? <fo:block start-indent="10mm" ...

    ??????? original H2 tag content

    ??? </fo:block>

    ?

    我們調(diào)用Xalan來應(yīng)用xhtml2fo.xsl。在調(diào)用Xalan之前,用Unix腳本xalan.sh來設(shè)置它需要用到的CLASSPATH變量。

    ?

    #/bin/sh

    ?

    export CLASSPATH='.;./lib/xalan.jar;./lib/xercesImpl.jar;./lib/xml-apis.jar;lib/serializer.jar'

    ?

    java -classpath $CLASSPATH org.apache.xalan.xslt.Process -IN $1 -XSL xhtml2fo.xsl -OUT $2 -tt

    ?

    因?yàn)?/span>Xalan需要一個(gè)XML解析器,所以這里還需要Apache Xercesxml-api JARs。所有的jar文件都可以在Xalan的發(fā)布包中找到。

    ?

    要通過對(duì)XHTML應(yīng)用樣式表來新建一個(gè)XSL-FO文件,可以調(diào)用腳本:

    ?

    ??? xalan.sh? hello.xml hello.fo

    ?

    我喜歡用Xalan的跟蹤開關(guān)(-tt)來顯示應(yīng)用的模板。hello.fo文件如下:

    ?

    <?xml version="1.0" encoding="UTF-8"?>

    ?

    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"

    ??? xmlns:html="http://www.w3.org/1999/xhtml"

    ??? writing-mode="lr-tb"

    ??? hyphenate="false"

    ??? text-align="start"

    ??? role="html:html">

    ?

    ? <fo:layout-master-set>

    ??? <fo:simple-page-master page-width="auto" page-height="auto"

    ?????????????????????????? master-name="all-pages">

    ????? <fo:region-body column-gap="12pt" column-count="1" margin-left="1in"

    ????????????????????? margin-bottom="1in" margin-right="1in" margin-top="1in"/>

    ????? <fo:region-before display-align="before" extent="1in"

    ??????????????????????? region-name="page-header"/>

    ????? <fo:region-after display-align="after" extent="1in"

    ????????????????????? region-name="page-footer"/>

    ????? <fo:region-start extent="1in"/>

    ????? <fo:region-end extent="1in"/>

    ??? </fo:simple-page-master>

    ? </fo:layout-master-set>

    ?

    ? <fo:page-sequence master-reference="all-pages">

    ??? <fo:title>Hello World

    ??? <fo:static-content flow-name="page-header">

    ????? <fo:block font-size="small" text-align="center" space-before="0.5in"

    ??????????????? space-before.conditionality=;"retain">

    ??????? Hello World

    ????? </fo:block>

    ??? </fo:static-content>

    ?

    ??? <fo:static-content flow-name="page-footer">

    ????? <fo:block font-size="small" text-align="center" space-after="0.5in"

    ??????????????? space-after.conditionality=quot;retain">

    ??????? - <fo:page-number/> -

    ????? </fo:block>

    ??? </fo:static-content>

    ?

    ??? <fo:flow flow-name="xsl-region-body">

    ????? <fo:block role="html:body">

    ??????? <fo:block space-before="1em" space-after="1em" role="html:p">

    ????????? Hello World!

    ??????? </fo:block>

    ????? </fo:block>

    ??? </fo:flow>

    ?

    ? </fo:page-sequence>

    ?

    </fo:root>

    ?

    ?

    第三步:XSL-FOPDF

    ?

    第三步,也就是最后一步,就是把XSL-FO文檔傳遞給格式化程序來生成PDF。我用的是Apache FOP(Formatting Objects Processor)FOP部分實(shí)現(xiàn)了XSL-FO標(biāo)準(zhǔn),并對(duì)PDF的輸出格式提供了最好的支持。而對(duì)Postscript還處于初級(jí)階段,對(duì)微軟的RTF的支持還在計(jì)劃中。FOP發(fā)布版包含shell腳本fop.sh/fop.bat,它們需要傳入XSL-FO文件作為輸入?yún)?shù)來生成目標(biāo)PDF文件。

    ?

    Unix下可以這樣運(yùn)行:

    ?

    ??? fop.sh hello.fo hello.pdf

    ?

    唯一所需的前提條件就是把設(shè)置為這個(gè)腳本使用到的FOP目錄設(shè)置環(huán)境變量。

    ?

    文件hello.pdf即為FOP的輸出,你在本文的源代碼中可以找到。

    ?

    因?yàn)?/span>FOP目前并未完全實(shí)現(xiàn)XSL-FO標(biāo)準(zhǔn),所以有一定的局限性。具體它實(shí)現(xiàn)了標(biāo)準(zhǔn)的哪些子集,可以在FOP的網(wǎng)站上的Compliance部分找到詳細(xì)說明。

    ---------------------------------------------------------------------------


    [譯注1] 此處原文是“在XML文件中的那個(gè)</p>JTidy自動(dòng)添加的”。我使用JTidy轉(zhuǎn)換的結(jié)果是</title>也被添加,而且這符合JTidy的邏輯,因此這里稍作了修改。

    ?

    [譯注2] 這一部分我在試著做的時(shí)候遇到很多問題。首先,有些地方作者描述的并不清楚,特別是對(duì)于模板的解釋那一部分。其次,在用Xalan做轉(zhuǎn)換時(shí)遇到了Connection time out的異常。這可能是由于xml文件中的dtd(xhtml1-strict.dtd)無法連接造成的。把該dtd下載到本地后,該異常即可消除。然后是無法找ent文件。所需要的這些ent都可以在xmlbuddy的安裝包里找到,拷過來就可以了。我不知道作者是不是沒有遇到過這些問題,也可能我這只是特例。


    評(píng)論

    # re: 轉(zhuǎn)換HTML內(nèi)容為PDF格式(1)  回復(fù)  更多評(píng)論   

    2006-05-30 17:27 by Fusion KISS
    這個(gè)轉(zhuǎn)換對(duì)html的標(biāo)簽都支持么?

    # re: 轉(zhuǎn)換HTML內(nèi)容為PDF格式(1)  回復(fù)  更多評(píng)論   

    2006-05-30 17:44 by shaofan
    @Fusion KISS

    這個(gè)你可以查一下那個(gè)xhtml2fo.xsl文件,看里面有沒有對(duì)你用到的那些標(biāo)簽的定義。如果不是很偏的話,我想大部分都是有的。不過你也可以重新定義。

    # re: 轉(zhuǎn)換HTML內(nèi)容為PDF格式(1)  回復(fù)  更多評(píng)論   

    2006-06-01 10:48 by for3w
    記得以前Dreamweaver4.0的時(shí)候就有一個(gè)插件,直接把HTML轉(zhuǎn)成PDF。

    # re: 轉(zhuǎn)換HTML內(nèi)容為PDF格式(1)  回復(fù)  更多評(píng)論   

    2006-06-01 18:20 by shaofan
    @for3w

    這兩種方法的區(qū)別在于,這里介紹的這種,可以把HTML->PDF的功能集成到你的JAVA代碼里,可以實(shí)現(xiàn)隨時(shí)的,對(duì)任意網(wǎng)頁的動(dòng)態(tài)轉(zhuǎn)換。之所以要?jiǎng)討B(tài),是因?yàn)榫W(wǎng)頁是動(dòng)態(tài)的,每次展示的內(nèi)容會(huì)不同。

    # re: 轉(zhuǎn)換HTML內(nèi)容為PDF格式(1)  回復(fù)  更多評(píng)論   

    2006-08-17 17:32 by 寧采臣
    xhtml2fo.xsl的確是一個(gè)好東西。
    不過我下載了一個(gè)想測(cè)試一下功能,使用apache的coconn框架想把html轉(zhuǎn)化為pdf,為了保證兼容性,我先把html轉(zhuǎn)化為xhtml,再用xhtml2fo.xsl對(duì)xhtml進(jìn)行轉(zhuǎn)化,可惜這里出錯(cuò)了。。還在研究中。。
    感謝樓主

    # re: 轉(zhuǎn)換HTML內(nèi)容為PDF格式(1)[未登錄]  回復(fù)  更多評(píng)論   

    2007-01-15 17:05 by Charles
    請(qǐng)問,你在轉(zhuǎn)換的過程中,pdf 的分頁是怎么控制的?
    謝謝~

    # re: 轉(zhuǎn)換HTML內(nèi)容為PDF格式(1)  回復(fù)  更多評(píng)論   

    2008-07-22 03:26 by Hi
    how to keep the blank lines
    主站蜘蛛池模板: 全部在线播放免费毛片| 亚洲高清不卡视频| 亚洲AV无码成H人在线观看| 天天看免费高清影视| 免费无码又黄又爽又刺激| 美女视频黄免费亚洲| 曰批全过程免费视频在线观看| 国产乱子精品免费视观看片| 日本免费一区二区在线观看| 最近中文字幕完整版免费高清| 久久午夜夜伦鲁鲁片免费无码影视| 成年人网站免费视频| 毛片网站免费在线观看| 在线A级毛片无码免费真人| 日本免费中文字幕在线看| 免费夜色污私人影院在线观看| 亚洲国产成人乱码精品女人久久久不卡 | 亚洲成A人片77777国产| 亚洲国产精品自产在线播放 | 国产成人免费在线| 国内精自视频品线六区免费| 成年女人色毛片免费看| 免费国产美女爽到喷出水来视频| 亚洲国产中文字幕在线观看 | 免费无码又爽又刺激高潮的视频 | 日本最新免费网站| 精品免费久久久久久成人影院| 免费a级毛片18以上观看精品| 亚洲一级黄色视频| 亚洲国产第一页www| 亚洲娇小性xxxx| 另类小说亚洲色图| 99久久99这里只有免费的精品| 免费人成视频在线观看网站 | 三级黄色片免费看| 51视频精品全部免费最新| 日韩精品免费电影| 亚洲老妈激情一区二区三区| 亚洲天堂一区在线| 精品女同一区二区三区免费播放| a级毛片免费观看视频|