linux word 转 pdf 下类似百度文库开发研究与实战
缘起
由于项目需要开发了类似百度文库和DOCIN类似的Flash播放器读取上传文档的系统,虽然最终技术问题都得以解决,但开发的过程中走了不少弯路,浪费了不少时间,特别是FlexPaper去掉自带的Logo这一步上,前后花了几天才得以解决,郁闷的有点不想在事后整理这个文档。为了让自己更好的记下解决问题的关键点所在,让同样有此需求的开发人员避免浪费时间,少走一些弯路,打起精神整理本次开发过程的技术研究和技术实现过程。
基于Linux环境的技术选型
项目基于Linux环境,如果你的服务器是windows,那么某些部分的技术选型跟本文可能会有出入,最主要的一个过程是把文档统一转成PDF,如果在Windows下可以考虑跳过,因为使用Flash Paper或者Print Flash可以把文档直接转成SWF。但这两者都不支持Linux环境。因此我们采用先把所有文档统一转成PDF,然后再转成SWF的方案。
主要技术点和步骤
基于对Linux环境的支持考虑(当然Windows下也可以采用这种技术选型),团队讨论决定引入openoffice sdk + pdf2swf tool,分两个步骤,先利用openoffice sdk把文档统一转成pdf,然后利用pdf2swf tool把pdf转成swf。
为了提高系统的反映速度并降低系统的耦合,主业务上传文档和文档转换流程采用异步处理(分开两个应用,降低耦合)、同时采用Concurrent多线程来处理文档转换(可以通过线程数的配置,统分利用cpu资源,提高并发处理速度,提高系统的反应时间),同时可以进一步扩展设计,以支持多台机器同时处理文档转换。
主要技术点实现
OpenOffice安装:
1)下载了3.3版本的OpenOffice
2)tar zxvf OOo_3.3.0_Linux_x86-64_install-rpm-wJRE_zh-CN.tar.gz
弯路:解压后,发现目录下有个可执行的setup,大喜,心想肯定执行setup安装了。
执行后抛出错误1:
“错误信息No X11 DISPLAY variable was set”
心想直接到本机操作是否会不同,结果还是一样的错误。
有大致的信息说要设置DISPLAY系统变量,又设置
export DISPLAY=localhost:0.0
再次执行抛出错误2:
抛出异常Exception in thread "main" java.lang.UnsatisfiedLinkError: /var/tmp/install_28877/usr/java/jre1.6.0_22/lib/amd64/xawt/libmawt.so
按提示给安装相关的图形界面包
yum install libXp
yum install libXtst
yum install libXp-devel
yum install libXau-devel
再次setup,又回到No X11 DISPLAY variable was set报错,我就想了,是不是当时装操作系统时没装上x windows server
也没想太多,搜了google,安装XWindow
yum groupinstall "X Window System"
yum groupinstall "GNOME Desktop Environment"
yum install nc expect
yum install freenx
yum groupinstall chinese-support
不放心,装好后reboot,再次进行setup,还是同样的出错。血压都升高了,心里在想难道要进图形界面才能安装么,但我们服务器原则上不推荐使用图形界面,如果安装的openoffice需要在图形界面下运行,那有点偏移了最初的技术选型目的。看来setup方式在文本界面下已经走不下去了,再次问google去,这次发现了另外一种安装方法。
3) cd OOO330_m20_native_packed-1_zh-CN.9567/RPMS/
4)rpm -ivh *.rpm
5)RPMS/desktop-integration
6)rpm -ivh openoffice.org3.3-redhat-menus-3.3-*.noarch.rpm
7)/opt/openoffice.org3/program/soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard &
前提:如果你没有安装jdk或者安装后没有把jdk/bin加入到PATH,则会报出找不到jre
vi /etc/profile,加入以下三行
export JAVA_HOME=/usr/local/jdk1.6.0_17
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=$JAVA_HOME/lib/tools.jar:$CLASSPATH
8)查看服务是否运行正常
netstat -ano 可以查看到8100端口被soffice服务占用
ps aux|grep soffice 查看进程
OpenOffice SDK3.3安装:
2)tar zxvf OOo-SDK_3.3.0_Linux_x86-64_install-rpm_en-US.tar.gz
3)cd OOO330_m20_native_packed-1_en-US.9567/RPMS/
4)rpm -vih *.rpm
2.Linux下安装pdf2swf Tool
中文支持安装:
cd /usr/share/xpdf/
wget ftp://ftp.foolabs.com/pub/xpdf/xpdf-chinese-simplified.tar.gz
wget http://www.nginxs.com/download/font.zip
3) tar zxvf xpdf-chinese-simplified.tar.gz
unzip font.zip
mv Gbsn00lp.ttf gkai00mp.ttf xpdf-chinese-simplified/CMap/
cd /usr/share/xpdf/xpdf-chinese-simplified
4)vi add-to-xpdfrc
内容如下:
cidToUnicode Adobe-GB1 /usr/share/xpdf/chinese-simplified/Adobe-GB1.cidToUnicode
unicodeMap ISO-2022-CN /usr/share/xpdf/chinese-simplified/ISO-2022-CN.unicodeMap
unicodeMap EUC-CN /usr/share/xpdf/chinese-simplified/EUC-CN.unicodeMap
unicodeMap GBK /usr/share/xpdf/chinese-simplified/GBK.unicodeMap
cMapDir Adobe-GB1 /usr/share/xpdf/chinese-simplified/CMap
toUnicodeDir /usr/share/xpdf/chinese-simplified/CMap
displayCIDFontTT Adobe-GB1 /usr/share/xpdf/chinese-simplified/CMap/gkai00mp.ttf
保存后退出
相关Lib包安装:
SwfTool安装:
1)cd /usr/local/
2)wget http://www.swftools.org/swftools-0.9.1.tar.gz
3)tar zxvf swftools-0.9.1.tar.gz
4)cd swftools-0.9.1
5)./configure
6)make
7)make install
8)测试一下是否可用
pdf2swf -o /path/output.swf -T -z -t -f /path/yourpdffile.pdf -s languagedir=/usr/share/xpdf/xpdf-chinese-simplified -s flashversion=9
小意外:
测试环境意外宕机重起后,运行pdf2swf,抛出错误:
pdf2swf: error while loading shared libraries: libjpeg.so.8: cannot open shared object file: No such file or directory
find了一遍,发现libjpeg.so.8被装到了/usr/local/lib下,这不是默认的/lib或/usr/lib目录。问了下google,对于动态库的路径问题有以下三种解决方案
动态库的路径问题
为了让执行程序顺利找到动态库,有三种方法:
(1)把库拷贝到/usr/lib和/lib目录下。
(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。例如动态库libhello.so在/home/ting/lib目录下,以bash为例,使用命令:
$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib
(3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见。
本人使用了第2种办法,再次运行pdf2swf正常。
pdf2swf参数很多,以下参数是经网友翻译后的详细说明:
指定转换的页面范围,使用的页码描述方法与打印机打印文件时候的选页一样
然后看看-s都可以设置些什么:
PDF Parameters:
PDF device global parameters:
fontdir= a directory with additional fonts 指定字体目录, 与1级参数的-F相若
font= an additional font filename 增加额外的字体文件
pages= the range of pages to convert (example: pages=1-100,210-) 指定页面范围,与1级参数的-p相若
zoom= the resolution (default: 72) 指定分辨率,默认为72dpi
languagedir= Add an xpdf language directory 增加一个xpdf的语言目录,对非西欧字符有用
multiply= Render everything at the resolution 在几倍分辨率下渲染
poly2bitmap Convert graphics to bitmaps 把其中的图形转成点阵
bitmap Convert everything to bitmaps 把所有内容转成点阵(包括字体)
SWF Parameters:
SWF layer options:
jpegsubpixels=<pixels> resolution adjustment for jpeg images (same as jpegdpi, but in pixels) jpeg图片的分辨率
ppmsubpixels=<pixels> resolution adjustment for lossless images (same asppmdpi, but in pixels) 无损图片的分辨率
subpixels=<pixels> shortcut for setting both jpegsubpixels and ppmsubpixels 快速设置上两个参数
drawonlyshapes convert everything to shapes (currently broken) 所有都转成图形
ignoredraworder allow to perform a few optimizations for creating smaller SWFs 允许执行一些小优化
linksopennewwindow make links open a new browser window 链接打开新窗口
linktarget target window name of new links 新链接窗口的名称
linkcolor=<color) color of links (format: RRGGBBAA) 链接的颜色
linknameurl Link buttons will be named like the URL they refer to (handy for iterating through links with actionscript) 链接名称与链接URL一致
storeallcharacters don’t reduce the fonts to used characters in the output file 保存所有的字符字体
enablezlib switch on zlib compression (also done if flashversion>=7) 使用zlib压缩
bboxvars store the bounding box of the SWF file in actionscript variables 在as中保存swf的区域大小
dots Take care to handle dots correctly 保存单点显示
reordertags=0/1 (default: 1) perform some tag optimizations 执行某些tag优化
internallinkfunction=<name> when the user clicks a internal link (to a different page) in the converted file, this actionscript function is called 内部链接函数,如果点击一个内部链接,将调用该actionscript函数
externallinkfunction=<name> when the user clicks an external link (e.g. http://www.foo.bar/) on the converted file, this actionscript function is called 外部链接函数,如果点击一个外部链接,将调用该actionscript函数
disable_polygon_conversion never convert strokes to polygons (will remove capstyles and joint styles) 不要将笔画转成多边形
caplinewidth=<width> the minimum thichness a line needs to have so that capstyles become visible (and are converted) 线条最低转换宽度,比这个细的线条将不转换
insertstop put an ActionScript “STOP” tag in every frame 在swf的每个桢中添加stop()函数
protect add a “protect” tag to the file, to prevent loadingin the Flash editor 增加protect标签,禁止在flash中加载该swf
flashversion=<version> the SWF fileversion (6) 设置最低swf版本
framerate=<fps> SWF framerate 设置桢率
minlinewidth=<width> convert horizontal/vertical boxes smaller than thiswidth to lines (0.05)将宽度少于某值的矩形转成线条
simpleviewer Add next/previous buttons to the SWF 使用简单的导航
animate insert a showframe tag after each placeobject (animate draw order of PDF files)
jpegquality=<quality> set compression quality of jpeg images 设置jpeg的压缩质量
splinequality=<value> Set the quality of spline convertion to value (0-100, default: 100). 设置样条曲线的转换质量
disablelinks Disable links. 禁止链接
3.文档转成PDF的Java实现
这篇文档http://blog.zhaojie.me/2010/05/convert-document-to-pdf-via-openoffice.html介绍的很详细,基本上可以照着上面做。虽然是windows下的例子,但在Linux下做稍微改动即可。这里就不贴出代码,列出这部分实现的细节差别:
在Linux下,所需相关类的路径如下:
/opt/openoffice.org/basis3.3/program/classes/unoil.jar
/opt/openoffice.org/ure/share/java/jurt.jar
/opt/openoffice.org/ure/share/java/juh.jar
/opt/openoffice.org/ure/share/java/bootstrapconnector.jar(按上述文档下载,并上传到这个目录)
代码片段:
?
?
?
?
?
?
新增传入端口参数,可以支持打开多个openoffice,如果线程多个,需要为每个线程提供不同的端口。
?
?
新增传入端口参数,可以支持打开多个openoffice,如果线程多个,需要为每个线程提供不同的端口。
?
?
?
输入文档不同,最好选择不同的打印方式,java代码实现
?
?
?
?
?
?
?
?
4.PDF转成SWF的Java实现
代码片段
?
?
?
?
代码片段说明:
(1)其中一些常量,可以自行定义,HXConstants.PDF2SWF_PATH、HXConstants.LANGDIR分别定义swftool安装路径、中文支持安装路径,为了移植方便,可以配置在properties配置文件里,如:
oosyspath=C:/Program Files/OpenOffice.org 3/program/
pdf2swf=D:/SWFTools/
langdir=/usr/share/xpdf/xpdf-chinese-simplified
发布到linux环境后,修改配置文件即可。
(2)根据需要,可以追加参数,参考本文pdf2swf Tool参数详细说明。
(3) pro.waitFor()与pro.exitValue()的区别,pro.exitValue可以异步处理,你可以根据需求而定。
5.Concurrent线程池实现
1)整体业务描述
(1)首先前台用户提交文档上传。
(2)后台异步处理上传文档。
2)Concurrent实现任务线程池
(1)实现抓取待处理的任务,放入BlockingQueue,避免重复抓取;
(2)实现多线程处理文档,线程从BlockingQueeu取出一个任务,处理完后把对应的文档数据置成处理完成状态。
见博客”一个任务队列的BlockingQueue实现”。
6.FlexPaper源码初步说明及改造
1)研究历程
FlexPaper是一套开源系统,用于展示swftool生成的swf,通过参数设置能达到比较好的展示效果,FlexPaper遵循GNU GPL协议。项目地址:
http://code.google.com/p/flexpaper/
源码地址:
http://flexpaper.googlecode.com/svn/trunk/
如果你不介意FlexPaper播放的时候带有其自带的logo标签,下载trunk下的Example,里面有详细的例子。如果你要对播放器进行一些修改,比如要去掉其自带的logo,那就要对FlexPaper进行重新编译。可以看到trunk下有FlexPaper项目和FlexPaper_SDK4项目,两者的区别主要是Flex SDK版本,前者使用Flex SDK3.5,后者使用Flex SDK4及以上版本,其次FlexPaper_SDK4在我下载时还只有beta版,下载后导入4.0对应的FlexPaper_SDK4.swc编译不能通过,因此当前时间还是选择FlexPaper项目比较稳妥。在Eclipse(安装Flash Builder插件)下通过svn检出项目即可,检出后会自动成了Flex库项目,完成编译后,可以看到在目录bin下编译产生的FlexPaper.swc文件。
提示: 如果在编译过程中报错:1046: 找不到类型,或者它不是编译时常数: Vector。
把Flex编译器Adobe Flash Player选项选种:使用特定版本(v):10.0.0,而Flex3.5默认的设置是9.0.0
还需要在资源加入local\{local},如果编译还报local错误,那就把local直接拷贝到sdks\3.5.0\frameworks\local下。
弯路1:本人一开始以为直接生成FlexPaperViewer.swf,对检出Flex库项目很纳闷,然后就自己创建Flash web类型的项目,再通过svn检出,又手动把项目下的FlexPaperViewer.mxml设置成默认运行,这样是生成了FlexPaperViewer.swf,但打开FlexPaperViewer.swf发现只有灰色或黑色的背景。在浪费大半天时间后,在同事提醒下才搞明白原来此FlexPaperViewer.mxml非Example项目下直接编译生成的FlexPaperViewer.swf。这该死的命名,对刚接触的人来说是太容易产生联想了。
弯路2:那就自己写一个FlexPaperViewer.mxml,或者干脆不要取这个名也可以,容易产生混淆。网上找到一个,引入重新编译过的FlexPaper.swc,编译成功,打开后界面也出来了。但在加载指定的SwfFile时一直加载不出来,右上角的菊花一直在转。怀疑是不是自编译的FlexPaper.swc有问题,上官网下载官方的FlexPaper.swc,还是不行……,周末后家里的开发环境又折腾了大半天,中间还下了个flash反编译软件,打算对Example下的FlexPaperView.swf进行反编译,可惜目前大多反编译软件只有反编译成.fla或.as,最终反编译也只能作罢。回到正路,终于找到比较完整的FlexPaperViewr.mxml。
右上方的Logo修改:在FlexPaper项目的FlexPaperView.xml做修改,很容易找到;
右下方的Logo修改:在FlexPaper项目的Viewer.as,主要对成员变量_skinImgDo做注释,其中有一行代码addChildAt改成addChildAt (_paperContainer,1);
重新编译后,再次编译FlexPaperView.xml,在FlexPaperView.html传入正确的SwfFile参数,右角的转动菊花终于消失了,看到了想要的效果。
FlexPaper去logo版本,去掉了logo及打印按钮,去掉了右键的自定义菜单http://download.csdn.net/detail/klo0704/4186008
?
?
yum install libXp
yum install libXtst
yum install libXp-devel
yum install libXau-devel
yum groupinstall "X Window System"
yum groupinstall "GNOME Desktop Environment"
yum install nc expect
yum install freenx
yum groupinstall chinese-support
2)Linux下openoffice服务需要启动
在linux,安装完openoffice后,需要启动soffice服务:/opt/openoffice.org3/program/soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;"
-nofirststartwizard & 如没有启动服务,执行文档转换服务会出错。