XML을 PDF문서로 만들기
Summary :
XML을 처음 접할 때 XML의 장점으로 많이 듣는 것중의 하나는 XML로 작성된 문서를 다양한 문서로 변환하기 쉽다는 것이다. XML로 작성된 문서는 적용하는 XSL에 따라 XML, HTML, WML, PDF등 다양한 형태의 문서로 변환할 수 있다. 하지만 국내 현실에서 XML공부할 때 HTML로만의 변환에만 치우치는 경향이 있어 XML을 PDF로 변환하는 방법에 대하여 알아본다. XML을 PDF로 변환할 때 해결하기 힘든 부분이 한글이 깨지는 문제이다. 따라서 한글문제에 대한 해결책도 제시한다.
XML을 처음 접할 때 XML의 장점으로 많이 듣는 것중의 하나는 XML로 작성된 문서를 다양한 문서로 변환하기 쉽다는 것이다. XML로 작성된 문서는 적용하는 XSL에 따라 XML, HTML, WML, PDF등 다양한 형태의 문서로 변환할 수 있다.
하지만 국내 현실에서 XML공부할 때 HTML로만의 변환에만 치우치는 경향이 있다. 따라서 XML문서를 PDF문서로 변환하는 방법에 대하여 살펴봄으로서 XML의 장점에 대하여 실감할 수 있을 것이다. PDF문서로 변환하기 위해 Apache Group에서 개발을 진행하고 있는 FOP를 이용하도록 하겠다.
많은 개발자들이 FOP를 이용하여 XML문서를 PDF만들려고 시도하고, 성공한 경우도 많이 보아왔다. 하지만 최종적으로 문제가 되는 것은 역시나 한글 문제였다. 자바지기를 운영하면서 PDF변환시 한글문제를 해결하지 못해 고민하는 개발자들을 많이 보아왔다. 필자역시 국내 웹을 돌아다니면서 한글문제를 해결하려고 노력했으나 해결책을 제시하는 곳이 거의 전무한 상태였다. 역시나 이런 경우에는 삽질(?)이 최고인듯하다. 삽질의 결과 한글문제를 해결할 수 있었고, 이번 강좌를 통하여 해결책을 제시하고자 한다.
FOP설치단계
1. http://xml.apache.org/fop/download.html에서 최신 FOP를 다운 받는다. FOP에 대하여 익숙하지 않은 개발자들은 http://xml.apache.org/fop/index.html에서 FOP에 대한 정보를 얻을 수 있다.
FOP(Formatting Objects Processor)에 대하여 간단하게 설명한다면 XML문서를 다양한 문서로 변환하기 Apache Group에서 개발하고 있는 세계 최초의 출력 Fomatter라고 FOP는 소개한다. 첫번째의 문서 변환이 PDF로 촛점이 맞추어져 있다.
2. 다운받은 ZIP파일을 개발자들이 원하는 적당한 곳에 압축을 푼다.(예: D:\XML\fop-0.20.4)
3. fop-0.20.4\build의 fop.jar와 fop-0.20.4\lib 아래에 5개의 jar파일(avalon-framework-cvs-20020315.jar, batik.jar, xalan-2.3.1.jar, xercesImpl-2.0.1.jar, xml-apis.jar)이 있을 것이다.
Prompt상에서 컴파일 및 테스트를 하고자 한다면 위 6개 파일을 클래스 패스에 추가한다. 이번 강좌는 Apache-Tomcat을 이용하여 애플리케이션내에서 테스트를 해볼 생각이다. Tomcat을 이용할 경우에는 "애플리케이션 이름\WEB-INF\lib"내에 사용하고자하는 jar파일을 추가할 경우 애플리케이션이 시작될 때 자동으로 로딩된다.
4. http://jakarta.apache.org/builds/jakarta-tomcat-4.0/release/v4.0.6/bin/에서 Tomcat 4.0.6을 다운 받는다. 다른 버전의 Tomcat을 다운받아도 된다. Tomcat에 대한 설치방법은 대부분의 개발자들이 알고 있을 것으로 생각되어 이 강좌에서는 생략한다.
5. Tomcat설치디렉토리\webapps\Application Name\WEB-INF\lib아래에 위 3에서 설명한 6개의 jar파일을 복사한다.
(예: D:\Server\Tomcat-4.0.6\webapps\xmlfop\WEB-INF\lib)
이상으로 FOP를 이용하여 XML문서를 PDF로 변환하기 위한 준비작업이 완료되었다. 이 강좌에서 진행하는 모든 소스는 테스트의 편의성을 위하여 JSP로 모두 작성할 생각이다. Servlet 및 클래스내에서 좀 더 유연하게 관리하는 방법은 계속되는 강좌에서 다룰 생각이다.
이번 강좌에서 진행할 모든 예제는 위에서 다운받은 FOP에 예제로 들어있는 파일들을 기준으로 설명하도록 하겠다. FOP문서의 작성 방법이나 PDF의 스키마에 대한 설명은 다른 문서를 참조하기 바란다. 이번 강좌는 작성되어 있는 FOP문서를 PDF로 변환하는 방법에 대하여만 살펴보도록 할 예정이다.
예제 파일들은 "fop-0.20.4\docs\examples"에 보면 다양한 예제들이 들어 있다. 이번 강좌에서는 "fop-0.20.4\docs\examples\fo"와 "fop-0.20.4\docs\examples\markers"에 있는 예제들을 이용하여 PDF문서로 변환해 보도록 하겠다. 이 강좌에서 사용되는 모든 소스는 첨부되는 파일로 제공될 것이다.
이번절에서는 FOP로 작성되어 있는 문서를 PDF로 변환하는 방법에 대하여 먼저 살펴보도록 하겠다. PDF문서를 작성하는 것이 어려워보이지만 작성되는 소스를 보면 상당히 간단하다고 느낄 수 있을 것이다.
<?xml version="1.0" encoding="utf-8"?> <fo:root font-family="Times Roman" font-size="12pt" text-align="center" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="simple" margin-top="75pt" margin-bottom="25pt" margin-left="100pt" margin-right="50pt"> <fo:region-body margin-bottom="50pt" background-image="file:../../graphics/xml_feather_transparent.gif"/> <fo:region-after extent="25pt"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="simple"> <fo:static-content flow-name="xsl-region-after"> <fo:block text-align-last="center" font-size="10pt"> <fo:page-number/> </fo:block> </fo:static-content> <fo:flow flow-name="xsl-region-body"> <fo:block font-size="18pt" font-weight="bold" background-color="#dddddd"> Simple example for background-image</fo:block> <fo:block font-size="14pt" space-before="1.6cm" text-align="end" white-space-collapse="false" background-repeat="no-repeat" background-image="file:../../graphics/linux.bmp"> background-image in a block 1 2 3 4 5 </fo:block> </fo:flow> </fo:page-sequence> </fo:root>
bgimage.fo : PDF문서로 변환할 FO파일
FO문서는 XML형태로 작성된다. 위 FO문서를 이해하기 힘들것이다. 이해하지 못해도 PDF문서를 만드는데는 크게 상관없다. 하지만 예제만으로의 PDF문서 생성이 아닌 개발자들이 원하는 PDF문서를 만들기 위해서는 FO태그들에 대하여 공부해야 할 것이다.
HTML문서도 원하는 스타일을 만들기 위하여 정해진 태그를 사용하듯이 FO문서도 원하는 PDF문서를 만들기 위하여 정해진 태그를 사용하게 되는 것이다.
<%@ page language="java" contentType="text/html;charset=euc-kr" %> <%@ page import="java.io.FileOutputStream"%> <%@ page import="org.xml.sax.InputSource"%> <%@ page import="org.apache.fop.apps.Driver"%> <% String path = "D:\\Server\\Tomcat-4.0.6\\webapps\\xmlfop\\fo\\"; String outputPath = path + "bgimage.pdf"; String inputPath = path + "bgimage.fo"; FileOutputStream fops = new FileOutputStream(outputPath); Driver driver=new Driver( new InputSource(inputPath), fops); driver.setRenderer(Driver.RENDER_PDF); driver.run(); fops.close(); out.println("PDF Create Success!!"); %>
fopToPDF.jsp : bgimage.fo문서를 bgimage.pdf로 변환하는 소스
위에서 본 bgimage.fo파일을 bgimage.pdf로 변환하는 소스파일이다. FO문서를 만드는 것이 어려운 작업이다. 하지만, 만들어진 FO문서를 PDF로 생성하는 것은 생각보다 간단하다.
http://localhost:8080/xmlfop/fopToPDF.jsp를 실행하여 PDF파일을 생성한다.
PDF문서를 만들기 위해서는 먼저 Driver를 생성한다. API를 참조하면 다양한 생성자가 있지만, Driver객체 생성시 입력 FO와 생성될 출력스트림을 위와 같이 정의할 수 있다. Driver의 setRenderer()를 이용하여 출력할 문서의 형태를 결정할 수 있다. 위는 "Driver.RENDER_PDF"를 전달하여 PDF문서임을 알 수 있다.
최종적으로 Driver의 run()메써드를 이용하여 FO파일을 PDF문서로 변환이 가능하다. 어려워 보이는 작업이였지만 쉽게 PDF문서를 생성할 수 있다. 위 소스에서 path를 개발자들의 path에 맞도록 수정만 해주면 테스트해볼 수 있다. fo 디렉토리 아래에 있는 다른 FO파일들을 이용하여 PDF문서를 생성해 보기바란다.
위 소스의 실행결과 생성된 PDF문서는 다음과 같다.
지금까지는 FOP문서를 PDF문서로 생성하는 방법에 대하여 살펴보았다. 그렇다면 XML문서를 XSL을 이용하여 PDF문서로 만드는 방법은 어떻게 될까? XML에 약간 친숙한 독자라면 이미 알고 있는 개발자들도 있을 것으로 생각된다. 우리가 XML문서를 HTML문서로 만들때를 생각해보자. XML태그에 HTML태그들이 없어도 XSL을 이용하면 HTML문서로의 변환이 가능하다. PDF문서를 작성할 때도 같다. XML문서에 XSL을 이용하여 FOP문서로 만들어 준 후 앞에서 살펴본 방식으로 PDF문서를 만들 수 있도록 하면 된다.
XML과 XSL에 익숙한 개발자라면 FOP문서를 만드는 것은 어렵지 않을 것이다. HTML문서를 만들기 위해 HTML 태그를 알아야 하듯이 PDF문서를 만들기 위해서는 FOP태그를 알아야 한다. FOP태그의 사용법에 대해서는 FOP사이트나 FOP관련문서를 참조하기 바란다.
앞으로 보게될 예제는 FOP에서 제공하는 예제를 이용하여 PDF문서를 작성해 보도록 하겠다.
<?xml version="1.0"?> <glossary> <term-entry> <term>basic-link</term> <definition>The fo:basic-link is used for representing the start resource of a simple link.</definition> </term-entry> <term-entry> <term>bidi-override</term> <definition>The fo:bidi-override inline formatting object is used where it is necessary to override the default Unicode-bidirectionality algorithm direction for different (or nested) inline scripts in mixed-language documents.</definition> </term-entry> ..중간생략.. <term-entry> <term>wrapper </term> <definition>The fo:wrapper formatting object is used to specify inherited properties for a group of formatting objects. It has no additional formatting semantics.</definition> </term-entry> </glossary>
glossary.xml : FOP에서 제공하는 예제 XML소스.
전체 소스 보기
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0"> <xsl:template match="glossary"> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="all" page-height="11.5in" page-width="8.5in" margin-top="1in" margin-bottom="1in" margin-left="0.75in" margin-right="0.75in"> <fo:region-body margin-top="1in" margin-bottom="0.75in"/> <fo:region-before extent="0.75in"/> <fo:region-after extent="0.5in"/> </fo:simple-page-master> </fo:layout-master-set> ..중간 생략.. <xsl:template match="definition"> <fo:block text-align="start" start-indent="2em"> <xsl:value-of select="."/> </fo:block> </xsl:template> </xsl:stylesheet>
glossary.xsl : FOP에서 제공하는 예제 XSL소스.
전체 소스 보기
PDF로 만들 XML과 XSL에 대하여 살펴보았다. 전체소스를 보고 싶다면 각 소스아래의 전체소스보기를 클릭하여 전체소스를 보기 바란다. 여기서는 각 소스의 일부분만을 제공했다.
XML문서는 이전까지 우리가 작성했던 XML문서와 같은 형태이다. 다른 부분은 XSL이다. 이전까지 XML을 HTML로 만들었던 개발자들이라면 XSL에서 HTML태그들을 사용했을 것이다. 그러나 위 예제의 glossary.xsl에서는 FO태그를 이용하여 스타일을 결정하고 있다.
<%@ page language="java" contentType="text/html;charset=euc-kr" %> <%@ page import="java.io.*"%> <%@ page import="javax.xml.transform.*"%> <%@ page import="javax.xml.transform.sax.*"%> <%@ page import="javax.xml.transform.stream.*"%> <%@ page import="org.apache.fop.apps.*"%> <% String path = "D:\\Server\\Tomcat-4.0.6\\webapps\\xmlfop\\markers\\"; String outputPath = path + "glossary.pdf"; String xml = path + "glossary.xml"; String xsl = path + "glossary.xsl"; FileOutputStream fops = new FileOutputStream(outputPath); Driver driver =new Driver(); driver.setOutputStream(fops); driver.setRenderer(Driver.RENDER_PDF); Transformer transformer=TransformerFactory.newInstance() .newTransformer(new StreamSource(new File(xsl))); transformer.transform(new StreamSource(new File(xml)), new SAXResult(driver.getContentHandler())); fops.close(); out.println("PDF Create Success!!"); %>
xmlToPDF.jsp : XML과 XSL을 이용하여 PDF문서를 생성하는 소스
http://localhost:8080/xmlfop/xmlToPDF.jsp를 실행하여 PDF파일을 생성한다. PDF파일이 정상적으로 생성될 것이다.
Driver를 이용하여 PDF를 생성하는 과정은 앞에서 FOP문서를 PDF문서로 생성할 때와 비슷하다. XML문서의 변환에서 한가지 추가된 것으로 XSL을 적용하여 FOP문서로 변환하는 과정이다. XSLT Processor는 Xalan이 이용되었다. Xalan XSLT Processor를 이용하여 XML문서를 HTML로 변환해본 개발자들이라면 위 예제를 쉽게 이해할 수 있을 것이다.
위 소스 실행결과 생성된 PDF문서는 다음과 같다.
지금까지 FO파일과 XML파일을 이용하여 PDF문서를 작성하는 방법에 대하여 살펴보았다. XML파일을 이용하여 PDF를 작성했던 예제에서 glossary.xml에 한글을 추가해보자. 한글을 추가한 다음 XML과 XSL문서 모두를 <?xml version="1.0" encoding="euc-kr"?gt;으로 수정한 다음 PDF문서로 재작성해보자. 한글이 깨져서 ??와 같이 깨져서 보일 것이다. HTML문서에서는 이와 같이 XML, XSL문서의 인코딩만을 바꿔서 한글 문제를 해결할 수 있었다. 그러나 PDF문서의 작성에서는 인코딩만으로는 해결할 수 없다.
위의 glossary.xsl에서 font-family="serif" 부분이 있듯이 PDF문서를 작성할 때는 한글 폰트를 지정해 주어야만 한글을 깨지지 않고 볼 수 있다.
따라서 한글 폰트를 만들어 한글로 작성된 PDF문서를 작성하는 방법에 대하여 살펴본다.
http://xml.apache.org/fop/fonts.html에서 폰트를 추가하는 방법에 대하여 다루고 있다. 이 문서를 기초로 한글 폰트를 추가하는 방법에 대하여 알아보도록 하겠다.
새로운 폰트를 추가하는 과정
1. http://korea.gnu.org/people/chsong/jikji/fonts/에 접속하여 nGulim.ttf를 다운받는다.
2. 다운받은 nGulim.ttf를 "C:\WINNT\Fonts"(Windows 2000의 경우)에 복사한다.
3. FOP의 압축을 푼 디렉토리로 이동하여 "java -cp build\fop.jar;lib\xercesImpl-2.0.1.jar;lib\xml-apis.jar;lib\xalan-2.3.1.jar;lib\batik.jar org.apache.fop.fonts.apps.TTFReader C:\WINNT\Fonts\nGulim.ttf nGulim.xml"의 내용을 담고 있는 Batch파일을 만든다.
만약 Batch파일에 익숙하지 않은 개발자라면 첨부되는 파일에서 AddFont.bat(xmlfop아래에 있다.)을 찾아 FOP설치디렉토리로 복사한다.
4. 생성한 Batch파일을 실행하면 ngulim.xml파일이 생성된다. 생성된 ngulim.xml파일을 "애플리케이션\font\"에 복사한다.
5. 폰트를 추가하기 위해서는"fop-0.20.4\conf"에 있는 userconfig.xml을 수정해야 한다. userconfig.xml을 "애플리케이션\conf\"로 복사한 다음 ngulim.xml을 추가해 주어야 한다.
userconfig.xml을 열어 다음 부분을 추가한다.
<font metrics-file="D:\Server\Tomcat-4.0.6\webapps\xmlfop\font\ngulim.xml" embed-file="C:\WINNT\Fonts\nGulim.ttf" kerning="yes"%gt;
<font-triplet name="ngulim" style="normal" weight="normal"/%gt;
<font-triplet name="ngulim" style="normal" weight="bold"/%gt;
</font>
metrics-file과 embed-file에는 ngulim.xml과 nGulim.ttf의 절대경로를 준다.
이상으로 새굴림폰트를 추가하기 위한 모든 준비가 완료되었다. 만약 위의 과정이 복잡하다고 생각하는 독자들이 있다면 첨부되는 파일에서 conf/userconfig.xml을 열어 자신의 컴퓨터 경로에 맞도록 수정해주면 쉽게 새로운 폰트를 추가할 수 있다.
다음은 새로운 폰트를 사용하는 소스에 대하여 살펴보자.
<%@ page language="java" contentType="text/html;charset=euc-kr" %> <%@ page import="java.io.*"%> <%@ page import="javax.xml.transform.*"%> <%@ page import="javax.xml.transform.sax.*"%> <%@ page import="javax.xml.transform.stream.*"%> <%@ page import="org.apache.fop.apps.*"%> <% String path = "D:\\Server\\Tomcat-4.0.6\\webapps\\xmlfop\\"; String outputPath = path + "hangulmarkers\\hangulglossary.pdf"; String xml = path + "hangulmarkers\\glossary.xml"; String xsl = path + "hangulmarkers\\glossary.xsl"; String userConfig = path + "conf\\userconfig.xml"; FileOutputStream fops = new FileOutputStream(outputPath); Options options = new Options(new File(userConfig)); Driver driver =new Driver(); driver.setOutputStream(fops); driver.setRenderer(Driver.RENDER_PDF); Transformer transformer=TransformerFactory.newInstance() .newTransformer(new StreamSource(new File(xsl))); transformer.transform(new StreamSource(new File(xml)), new SAXResult(driver.getContentHandler())); fops.close(); out.println("PDF Create Success!!"); %>
hangulXMLToPDF.jsp : 새로 추가한 한글 폰트를 사용하는 소스
XML과 XSL은 앞에서 사용한 glossary.xml과 glossary.xsl을 재사용하도록 하겠다. 이 두 파일을 수정해야하기 때문에 수정된 XML, XSL소스는 hangulmarkers디렉토리에서 제공한다.
새로 추가된 한글 폰트를 사용하는 방법은 Options options = new Options(new File(userConfig));부분만 추가하면 된다.
XML과 XSL의 인코딩을 euc-kr로 수정한 다음 XSL에서 ngulim폰트를 사용할 수 있도록 font-family="ngulim"와 같이 font-family항목을 모두 바꾸어 준다.
이제 한글 PDF를 작성하기 위한 모든 준비작업이 끝났다. http://localhost:8080/xmlfop/hangulXMLToPDF.jsp를 실행하여 PDF문서가 정상적으로 만들어 지는지 확인해보자.
아마도 NullpointerException이 발생할 것이다. NullpointerException가 발생하는 원인을 찾아보면 "Failed to read font metrics file D:\Server\Tomcat-4.0.6\webapps\xmlfop\f
ont\ngulim.xml : An invalid XML character (Unicode: 0x0) was found in the elemen
t content of the document."에서 찾을 수 있다. ngulim.xml에 인코딩에 맞지않는 문자가 들어가 있다는 것이다. 필자가 삽질을 시작한 것은 여기서 부터이다. 도저히 이유를 찾지못했다. ngulim.xml의 인코딩을 바꿔보기도 했지만 원인을 찾기가 쉽지 않았다.
역시나 원인은 ngulim.xml에 있었다. ngulim.xml을 열어 처음의 <font-name>NGulim</font-name>처럼 바꾸어 준다. font-name에 들어있는 문자가 인코딩에 맞지 않는 것이였다. 몇일의 삽질끝에 발견했다. 그 때의 기쁨이란 프로그램을 하는 개발자들이라면 모두 느꼈을 것이다.
http://localhost:8080/xmlfop/hangulXMLToPDF.jsp를 실행하여 PDF를 생성해본다. glossary.xml에 한글을 추가하여 한글이 정상적으로 PDF로 생성되는지를 확인해 본다. 위 과정이 맞았다면 정상정으로 생성될 것이다.
한글로 생성된 PDF파일은 다음과 같다.
지금까지 FOP, XML을 이용하여 PDF문서를 작성하는 방법에 대하여 살펴보았다. PDF를 작성할 때 가장 큰 걸림돌이였던 한글문제에 대해서도 새로운 폰트를 추가하여 해결할 수 있었다. 위에서 사용한 모든 소스는 fopxml.war로 제공될 것이다. 이 파일을 tomcat\webapps\에 넣으면 지금까지의 테스트를 손쉽게 해볼 수 있을 것이다. 수정할 부분은 JSP파일에서 fo와 xml파일들을 경로를 수정해 주기만 하면 된다.
앞으로 FOP강좌에서는 클래스 내에서 위 작업을 손쉽게 할 수 있도록 수정해볼 것이다. 또한 PDF에서 SVG파일을 사용하는 방법에 대하여도 살펴볼 것이다. 만약 여유가 된다면 FOP에서 사용하는 태그에 대하여도 살펴볼 생각이다.
앞으로도 많은 개발자들의 관심이 있기를 바란다. 모든 질문은 XML Q&A란을 이용해주기 바란다. 많은 개발자들과의 정보공유를 위하여 꼭 게시판을 이용해 주기바란다. E-mail을 통한 질문은 삼가해 주기를 바란다.
참고 자료 :
- 강좌에서 사용한 소스 :
FOP강좌 소스 - FOP사이트 :
http://xml.apache.org/fop/index.html - FOP에 대하여 더 깊이 있게 알 수 있는 문서 :
http://ibiblio.org/xml/books/bible2/chapters/ch18.html#d1e6582 - Xalan XSLT Processor관련 사이트 :
http://xml.apache.org/xalan-j/index.html - Jakarta Tomcat을 다운 받을 수 있는 곳 :
http://jakarta.apache.org - 굴림, 바탕체를 다운 받을 수 있는 곳 :
http://korea.gnu.org/people/chsong/jikji/fonts/
댓글 없음:
댓글 쓰기