2005년 10월 24일 월요일

[펌] oracle column 길이 구하는 방법

 

1. 보통의 varchar

select length( content ) ... ;

2. BLOB type

SELECT Dbms_Lob.getlength(CONTENT_BLOB) as CONTENT_SIZE, ...


3. LONG RAW type

select length(content) 로는 ORA-00932: inconsistent datatypes  에러가 발생한다.
아래의 PL/SQL 을 작성하여 구해야한다.

1. Single Record 에 대한 예

TO 테이블의 Description 컬럼 길이를 구하는 경우

$ vi len_long.sql
declare
  length_var NUMBER;
  cursor TOY_CURSOR is
    select * from TO;
  toy_val TOY_CURSOR%ROWTYPE;
begin
  open TOY_CURSOR;
  fetch TOY_CURSOR into toy_val;
    length_var := LENGTH(toy_val.Description);
    DBMS_OUTPUT.PUT_LINE('Length of Description: '||length_var);
  close TOY_CURSOR;
end;
/

SQL> set serveroutput on
SQL> @len_long
Length of description : 21

PL/SQL procedure successfully completed.

2. Multiple Record 에 대해서는 cursor FOR Loop 를 사용한다.

$ vi len_long.sql
declare
  length_var NUMBER;
  cursor TOY_CURSOR is
    select * from TOY;
  toy_val TOY_CURSOR%ROWTYPE;
begin
  for toy_val in TOY_CURSOR loop
    length_var := LENGTH(toy_val.Description);
    DBMS_OUTPUT.PUT_LINE('ID: '||toy_val.Toy_ID);
    DBMS_OUTPUT.PUT_LINE('Length of Description: '||length_var);
  end loop;
end;
/

SQL> set serveroutput on
SQL> @len_long  
ID: 1
Length of Description: 21
ID: 2
Length of Description: 27

PL/SQL procedure successfully completed.

[펌] Dictionary를 조회할 때 매우 유용한 SQL문

Dictionary를 조회할 때 매우 유용한 SQL문

select * from dict
where lower(comments) like lower('%&dictionary_comments%');
--------------------------------------
select * from dict
where lower(table_name) like lower('%&dictionary_name%');
--------------------------------------
column table_name format a30
column column_name format a30
column comments format a60 word_wrapped
select * from dict_columns
where lower(table_name) like lower('&dictionary_name')
and lower(column_name) like lower('&column_name');
--------------------------------------
select * from dict_columns
where lower(table_name) like lower('&dictionary_name');

View 모음

SELECT TABLE_NAME, CONSTRAINT_NAME, CONSTRAINT_TYPE, R_CONSTRAINT_NAME, SEARCH_CONDITION
FROM USER_CONSTRAINTS;
-----------------------------------
SELECT TABLE_NAME, CONSTRAINT_NAME, CONSTRAINT_TYPE, STATUS, VALIDATED, DEFERRABLE, DEFERRED
FROM USER_CONSTRAINTS;
-----------------------------------
SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME
FROM USER_CONS_COLUMNS;
-----------------------------------
SELECT A.TABLE_NAME, B.COLUMN_NAME, A.CONSTRAINT_NAME,
A.CONSTRAINT_TYPE, A.R_CONSTRAINT_NAME, A.SEARCH_CONDITION
FROM USER_CONSTRAINTS A, USER_CONS_COLUMNS B
WHERE A.CONSTRAINT_NAME = B.CONSTRAINT_NAME;
-----------------------------------
SELECT T.TABLE_NAME, T.CONSTRAINT_NAME, C.COLUMN_NAME,
T.DEFERRABLE, T.DEFERRED, T.VALIDATED
FROM DBA_CONSTRAINTS T, DBA_CONS_COLUMNS C
WHERE T.CONSTRAINT_NAME = C.CONSTRAINT_NAME
AND T.OWNER LIKE '%&OWNER%'
AND T.TABLE_NAME LIKE '%&TABLE_NAME%';
-----------------------------------
SELECT OBJECT_NAME, OBJECT_TYPE, OBJECT_ID, DATA_OBJECT_ID, CREATED, TIMESTAMP, LAST_DDL_TIME
FROM USER_OBJECTS
ORDER BY OBJECT_TYPE;
----------------------------------
SELECT VIEW_NAME, TEXT
FROM USER_VIEWS;
-----------------------------------
SELECT *
FROM USER_SEQUENCES;
-----------------------------------
SELECT SYNONYM_NAME, TABLE_OWNER, TABLE_NAME
FROM USER_SYNONYMS;
-----------------------------------
SELECT TABLESPACE_NAME, INITIAL_EXTENT, NEXT_EXTENT, MIN_EXTENTS, MAX_EXTENTS,
PCT_INCREASE, MIN_EXTLEN, STATUS, CONTENTS, ALLOCATION_TYPE
FROM DBA_TABLESPACES;
----------------------------------
롤백세그먼트의 일반적인 정보
SELECT SEGMENT_NAME, OWNER, TABLESPACE_NAME,
INITIAL_EXTENT, NEXT_EXTENT, MIN_EXTENTS, STATUS
FROM DBA_ROLLBACK_SEGS;
----------------------------------
현재 인스턴트가 사용하고 있는 롤백세그먼트에 대한 통계 검색
SELECT N.NAME, S.EXTENTS, S.RSSIZE, S.OPTSIZE, S.HWMSIZE, S.XACTS, S.STATUS
FROM V$ROLLNAME N, V$ROLLSTAT S
WHERE N.USN = S.USN;
----------------------------------
롤백세그먼트에 대한 경합
SELECT N.NAME, ROUND(100*S.WAITS/S.GETS)
FROM V$ROLLNAME N, V$ROLLSTAT S
WHERE N.USN = S.USN;
----------------------------------
블록킹 트랜잭션
SELECT S.SID, S.SERIAL#, T.START_TIME, T.XIDUSN, S.USERNAME
FROM V$SESSION S, V$TRANSACTION T, V$ROLLSTAT R
WHERE S.SADDR = T.SES_ADDR
AND T.XIDUSN = R.USN
AND ((R.CUREXT = T.START_UEXT-1)
OR ((R.CUREXT = R.EXTENTS-1) AND T.START_UEXT = 0));
----------------------------------
인덱스에 대한 유효성 확인
SELECT BLOCKS, BTREE_SPACE, USED_SPACE, PCT_USED, LF_ROWS, DEL_LF_ROWS
FROM INDEX_STATS;
----------------------------------
SELECT USERNAME, USER_ID, DEFAULT_TABLESPACE, TEMPORARY_TABLESPACE, PROFILE
FROM DBA_USERS;
----------------------------------
SELECT TABLESPACE_NAME, BYTES, MAX_BYTES, BLOCKS, MAX_BLOCKS
FROM USER_TS_QUOTAS;
-----------------------------------
SELECT TABLESPACE_NAME, FILE_ID, FILE_NAME, BYTES,
AUTOEXTENSIBLE, INCREMENT_BY, MAXBYTES
FROM DBA_DATA_FILES;
-----------------------------------
SELECT FILE#, STATUS, RFILE#, BYTES, BLOCKS, CREATE_BYTES, BLOCK_SIZE, NAME
FROM V$DATAFILE;
-----------------------------------
SELECT FILE#, STATUS
FROM V$BACKUP;
-----------------------------------
SELECT *
FROM DBA_FREE_SPACE
ORDER BY BLOCK_ID;
-----------------------------------
SELECT A.TABLESPACE_NAME, A.BYTES, A.STATUS, B.STATUS, B.ENABLED, B.NAME
FROM DBA_DATA_FILES A, V$DATAFILE B
WHERE A.FILE_ID = B.FILE# AND A.TABLESPACE_NAME LIKE '%&TABLESPACE_NAME%';
-----------------------------------
SELECT TABLESPACE_NAME, EXTENTS_COALESCED, TOTAL_BYTES, BYTES_COALESCED
FROM DBA_FREE_SPACE_COALESCED;
-----------------------------------
SELECT A.TABLESPACE_NAME "TABLESPACE",
B.FILE_NAME "FILE",
B.BYTES "TOTAL SIZE",
C.BYTES "SIZE LEFT"
FROM DBA_TABLESPACES A, DBA_DATA_FILES B, DBA_FREE_SPACE C
WHERE A.TABLESPACE_NAME = B.TABLESPACE_NAME AND
A.TABLESPACE_NAME = C.TABLESPACE_NAME AND
A.TABLESPACE_NAME LIKE '%&TABLESPACE_NAME%';
-----------------------------------
SELECT SEGMENT_NAME, SEGMENT_TYPE, TABLESPACE_NAME, BYTES, BLOCKS, EXTENTS, INITIAL_EXTENT, NEXT_EXTENT
FROM USER_SEGMENTS;
-----------------------------------
SELECT INDEX_NAME, TABLE_NAME, UNIQUENESS, INDEX_TYPE, TABLESPACE_NAME
FROM USER_INDEXES;
-----------------------------------
SELECT LINE, POSITION, TEXT
FROM USER_ERRORS
ORDER BY SEQUENCE;
-----------------------------------
SELECT *
FROM USER_SOURCE;
-----------------------------------
SELECT SEGMENT_NAME, PARTITION_NAME, TABLESPACE_NAME, EXTENT_ID, BYTES, BLOCKS
FROM USER_EXTENTS
ORDER BY SEGMENT_NAME, EXTENT_ID;
-----------------------------------
SELECT TABLESPACE_NAME, SEGMENT_NAME, FILE_ID, EXTENT_ID, BLOCK_ID, BLOCKS
FROM DBA_EXTENTS
ORDER BY EXTENT_ID;
-----------------------------------
SELECT OPERATION, OPTIONS, OBJECT_NAME, ID, PARENT_ID, POSITION
FROM PLAN_TABLE
ORDER BY ID;
-----------------------------------
SELECT NUM_ROWS, BLOCKS, EMPTY_BLOCKS, AVG_SPACE, CHAIN_CNT, AVG_ROW_LEN
FROM USER_TABLES;
-----------------------------------
SELECT COLUMN_NAME, NUM_DISTINCT, LOW_VALUE, HIGH_VALUE, DENSITY
FROM USER_TAB_COLUMNS;
-----------------------------------
SELECT TABLE_NAME, TABLE_TYPE_NAME, PARENT_TABLE_NAME, PARENT_TABLE_COLUMN
FROM USER_NESTED_TABLES;
-----------------------------------
SELECT *
FROM DBA_PROFILES;
-----------------------------------
SELECT *
FROM USER_RESOURCE_LIMITS;
-----------------------------------
SELECT *
FROM USER_PASSWORD_LIMITS;
-----------------------------------
SELECT *
FROM V$OPTION;
-----------------------------------
SELECT *
FROM V$PARAMETER;
-----------------------------------
SELECT DISTINCT OBJECT_TYPE
FROM DBA_OBJECTS;
-----------------------------------
SELECT OWNER, OBJECT_NAME, SUBOBJECT_NAME, OBJECT_TYPE, CREATED
FROM DBA_OBJECTS
WHERE OWNER LIKE '%&OWNER%'
AND OBJECT_TYPE LIKE '%&OBJECT_TYPE%'
ORDER BY OBJECT_NAME;
-----------------------------------
SELECT OWNER, TABLE_NAME, TABLESPACE_NAME
FROM DBA_TABLES
WHERE TABLE_NAME LIKE '%&TABLE_NAME%'
ORDER BY OWNER, TABLE_NAME;
-----------------------------------
SELECT VIEW_NAME, TEXT
FROM DBA_VIEWS
WHERE VIEW_NAME LIKE '%&VIEW_NAME%';
-----------------------------------
SELECT *
FROM DBA_SYS_PRIVS
WHERE GRANTEE LIKE '%&ROLE%';
-----------------------------------
SELECT OWNER, SYNONYM_NAME, TABLE_OWNER, TABLE_NAME
FROM DBA_SYNONYMS
ORDER BY 2, 1, 3;
-----------------------------------
<클러스터>

클러스터의 일반적인 정보(블록파라미터..)
SELECT * FROM DBA_CLUSTERS
WHERE OWNER LIKE '%&OWNER%';

클러스터 테이블 및 클러스터 키 조회
SELECT OWNER, CLUSTER_NAME, CLU_COLUMN_NAME, TABLE_NAME
FROM DBA_CLU_COLUMNS
WHERE OWNER LIKE '%&OWNER%';

SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE
FROM DBA_TAB_COLUMNS
WHERE OWNER LIKE '%&OWNER%';
-----------------------------------
IOT 조회
SELECT T.TABLE_NAME AS "IOT", O.TABLE_NAME AS "OVERFLOW",
I.INDEX_NAME AS "INDEX", O.TABLESPACE_NAME AS "OVERFLOW TBS",
I.TABLESPACE_NAME AS "INDEX TBS", I.PCT_THRESHOLD
FROM DBA_TABLES T, DBA_TABLES O, DBA_INDEXES I
WHERE T.OWNER = O.OWNER
AND T.TABLE_NAME = O.IOT_NAME
AND T.OWNER = I.OWNER
AND T.TABLE_NAME = I.TABLE_NAME
AND T.OWNER LIKE '%&OWNER%';
-----------------------------------
SELECT USERNAME, TIMESTAMP, ACTION_NAME
FROM DBA_AUDIT_TRAIL;
-----------------------------------
SELECT USERID, OBJ$NAME, SES$ACTIONS, TIMESTAMP#
FROM SYS.AUD$
WHERE OBJ$NAME LIKE '%&OBJECT_NAME%';
-----------------------------------
SELECT SESSIONID, STATEMENT, TIMESTAMP#, USERID, TERMINAL, ACTION#,
OBJ$CREATOR, OBJ$NAME, SES$ACTIONS, COMMENT$TEXT, SPARE1
FROM SYS.AUD$;

[펌] 중요한 view들

분류

성능뷰 / 딕셔너리

딕셔너리

세션과 관련된 정보

V$SESSION

세션에 대한 전반적인 정보를 보여준다

V$SESSSTAT

세션의 현황에 대한 통계정보를 보여준다

V$SESSION_WAIT

세션의 WAITING 통계정보를 보여준다

V$SESSION_EVENT

세션의 현재 WATING EVENT를 보여준다

V$SESS_IO

세션의 IO현황을 보여준다

V$STATNAME

SESSSTAT STATUS의 이름을 보여준다.

성능 관련 정보

V$SYSTAT

시스템 전반의 성능 통계 정보를 보여준다

V$SYSTEM_EVENT

시스템의 WATING EVENT별 통계정보를 보여준다

V$LIBRARYCACHE

라이브러리 캐쉬 사용 정보를 보여준다.

V$ROWCACHE

데이터 딕셔너리의 사용정보를 보여준다

V$LATCH

LATCH에 대한 정보를 보여준다

V$LOCK

LOCK에 대한 정보를 보여준다

V$LOCKED_OBJECT

LOCK이 걸린 오브젝트에 대한 정보를 보여준다

V$SQLAREA

SQLAREA에 대한 정보를 보여준다

V$WAITSTAT

시스템의 현재 Waiting현황을 보여준다

SQL관련

V$SQL

Parse SQL문장을 보여줌

V$SQLTEXT

라인별로 SQL문장을 보여줌

V$SQLTEXT_WITH_NEWLINES

NewLine을 포함하여 SQL문장을 보여줌

시스템
구성정보

V$SGA

SGA 의 정보를 보여준다

V$PARAMETER

InitSID.ora 등에서 설정된 파라메터, 즉 데이터베이스의 구동되었을 때의 환경 파라메터 정보이다

V$CONTROLFILE

Control 파일에 대한 정보를 보여준다.

V$DATAFILE

데이터 파일에 대한 정보를 보여준다.

V$LOG, V$LOGFILE

리두 로그에 대한 정보를 보여준다.

USER

DBA_USERS

데이터베이스 USER에 대한 정보를 보여준다

권한

DBA_ROLES

ROLE에 대한 정보를 보여준다.

DBA_TAB_PRIVS

테이블에 대한 권한이 설정된 정보를 보여 준다

DBA_SYS_PRIVS

SYSTEM 권한이 설정된 정보를 보여준다

DBA_ROLE_PRIVS

ROLE에 대한 권한이 설정된 정보를 보여 준다.

DBA_COL_PRIVS

컬럼 단위로 권한이 설정된 정보를 보여준다.

세그먼트&오브젝트

DBA_SEGMENTS

세그먼트(저장공간이 있는 오브젝트)에 대한 정보를 보여준다.

DBA_OBJECTS

모든 오브젝트에 대한 정보를 보여준다.

테이블

스페이스

V$TABLESPACE

테이블 스페이스에 대한 정보를 보여준다.

DBA_TABLESPACES

테이블 스페이스에 대한 정보를 보여준다.

DBA_DATA_FILES

테이블스페이스를 구성하고 있는 데이터 파일에 대한 정보를 보여준다.

DBA_FREE_SPACE

아직 사용되지 않은 영역에 대한 정보를 보여준다.

DBA_EXTENTS

할당된  EXTENT의 정보를 보여준다.

DBA_TS_QUOTAS

QUOTA가 설정된 정보를 보여준다

테이블

DBA_TABLES

테이블에 대한 정보를 보여준다.

DBA_TAB_COLUMNS

테이블을 구성하는 컬럼에 대한 정보를 보여준다

DBA_TAB_COMMENTS

테이블의 설명에 대한 정보를 보여준다

DBA_PART_TABLES

파티션 테이블에 대한 정보를 보여준다.

DBA_PART_KEY_COLUMNS

파티션을 구성하는 기준 컬럼에 대한 정보를 보여준다

DBA_COL_COMMENTS

컬럼에 대한 설명에 대한 정보를 보여 준다

인덱스

DBA_INDEXES

인덱스에 대한 정보를 보여준다.

DBA_PART_INDEXES

파티션된 인덱스에 대한 정보를 보여준다

DBA_IND_COLUMNS

인덱스를 구성하는 컬럼에 대한 정보를 보여준다

CONSTRAINT

DBA_CONSTRAINTS

테이블에 걸려있는 제약조건을 보여준다.

DBA_CONS_COLUMNS

제약조건을 구성하는 컬럼에 대한 조건을 보여준다.

DBA_VIEWS

VIEW를 정의한 정보를 보여준다.

시노님

DBA_SYNONYMS

시노님에 대한 정보를 보여준다.

시퀀스

DBA_SEQUENCES

시퀀스에 대한 정보를 보여준다.

DB LINK

DBA_DB_LINKS

DB 링크에 대한 정의를 보여준다

트리거

DBA_TRIGGERS

트리거에 대한 정의를 보여준다.

DBA_TRIGGER_COLS

컬럼 단위로 작성된 트리거에 대한 정의를 보여준다.

ROLLBACK

DBA_ROLLBACK_SEGS

롤백세그먼트에 대한 정보를 보여 준다.

FUNCTION, PROCEDURE,

PACKAGE

DBA_SOURCE

FUNCTION, PROCEDURE,PACKAGE를 구성하는 PL/SQL 소스코드를 보여준다

[펌] default 값 확인

SELECT *                   -- data_default 컬럼
FROM dba_TAB_COLUMNS
where table_name= '테이블 명'

 

 

아 난 정말 바보인가보다..ㅠ.ㅠ

 

 

참고로 default  값을 삭제하고 싶으면

Alter Table 테이블 Modify(column  column type   default null);

2005년 10월 21일 금요일

[펌] 블루마인드 X-Internet 기반 우리은행연수관리시스템 개발

X-Internet / RIA 전문 개발 업체인 블루마인드 커뮤니케이션 (http://bluemind.co.kr)에서 우리은행 사내 인사연수 시스템을 X- Internet / RIA기반으로 개발했다고 밝혔다.

전세계 98%이상 컴퓨터에 탑재되어 있는 플래시를 기반으로 구동되기 에다양한 OS와 디바이스에서 구동이 가능하고 기존 HTML의 사용 불편 함과 C/S의 유지보수와 운영 환경의 제한 단점등을 해결해 사용자에게 편리한 인터페이스 환경과 빠른 구동 속도를 제공해 주고 있다.

자체 UI 프레임워크하에 개발된 우리은행 사내 인사연수 시스템에 들어간 플래시 어플리케이션 모듈은 컨테이너가 230여개가 될 정도로 복잡한 규모이며 "현재 국내외의 플래시기반 어플리케이션 규모중 가장 큰 사례" 가 될 것이라고 밝혔다.
간단한 예를 들면 블루마인드에서 개발한 극장 예약에 적용하는 RIA 의 경우(www.cinus.co.kr)구성 컨테이너가 보통 6개정도이고 복잡할 경우 10개정도로 컨테이너가 구성된다.

이와 같은 시스템의 성공적인 개발 적용을 통해 기존에 극장 예약 등에 적용되는 형태에서 다양한 기업의 비즈니스 업무에서 성공적인 확대 적용이 가능할 것이다.


또한 연수 담당자들과 시스템 운영 설명회를 가지면서 담당자들의 의견을 들어본 결과 시스템 운영 담당자의 경우 실제 4배에서 느낌상으로는 40배정도 어플리케이션 속도가 개선이 되었다고 이야기를 하고 있으며 실질적으로 이것을 사용해본 우리은행 직원들 대부분도 인터페이스의 개선 효과도 있지만 그보다는 이전에 만든 HTML 기반 어플리케이션에 비해 속도가 놀랄만큼 빨라졌다고 인정하고 있었다.
업무 개선효과 면에서도 예전에 1시간 넘게 걸리던 작업이 10분도 채 걸리지 않게 되어서 좀더 효율적인 작업 환경이 이루어졌다고 밝혔다.


블루마인드 커뮤니케이션의 CEO인 김태식 대표는 "우리은행 사내 인사 연수 시스템을 통해 플래시 기반 X-Internet 어플리케이션의 우수성이 입증되었기에 다양한 비즈니스에 적용 확대가 될 것으로 기대한다"고 말했다. 우리은행 사내 인사연수 시스템의 데모는

http://www.bluemind.co.kr/woori 에서 볼 수 있다.

출처 : http://itwarehouse.co.kr/

2005년 10월 17일 월요일

XML 대소문자 변환하기

 

XSLT Case Conversion Solution 

The following article was written by Erich Von Hauske N. after a discussion on XSLTalk.  Corey Haines started a tread on how to do recursion in XSLT, which lead to this solution that Erich and Corey came up with. 

Download the source

The solution of a problem like case conversion can take many forms, especially when you use a language like XSLT, which gives you the flexibility to give many solutions; each one with a different degree of recursiveness. The solution presented in this document is a middle-ground solution, it's recursive, but not as recursive as it could get, nevertheless it has a better performance than the completely recursive solution.

What we are demonstrating is firstly how to convert your XML data from on case to another.  As XML is case sensitive, when you need to do searches on your data, using the translate function demonstrated enables case-insensitive searches.  Then we are demonstrating a solution for implementing proper case converting - which demonstrates the recursion.

Upper Case Transformation

Upper and lower case transformations are very easy in XSLT thanks to the translate function. An alternative solution could be to implement the transformation and switch character by character, but since XSLT already gives us a better solution, let's better take advantage of it.  Please find the full source in the downloadable file.

Firstly lets place all the letters of the alphabet, lower case and upper case in variables.

<xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
To then convert our data to upper case we use the translate method, which replaces all the lower case characters with upper case.
<xsl:value-of select="translate($toconvert,$lcletters,$ucletters)"/>

Lower Case Transformation

The lower case transformation is basically the same:

<xsl:value-of select="translate($toconvert,$ucletters,$lcletters)"/>

Proper Case Transformation

The interesting part of this exercise is really in here: the proper case transformation. The presented solution first translates the entire string to lower case, and after that it translate only the first letter of every word to upper case. Here's the code:

<xsl:template name='convertpropercase'>
<xsl:param name='toconvert' />

<xsl:if test="string-length($toconvert) > 0">
	<xsl:variable name='f' select='substring($toconvert, 1, 1)' />
	<xsl:variable name='s' select='substring($toconvert, 2)' />
	
	<xsl:call-template name='convertcase'>
		<xsl:with-param name='toconvert' select='$f' />
		<xsl:with-param name='conversion'>upper</xsl:with-param>
	</xsl:call-template>

<xsl:choose>
	<xsl:when test="contains($s,' ')">
		<xsl:value-of select='substring-before($s," ")'/>
		<xsl:text> </xsl:text>
		<xsl:call-template name='convertpropercase'>
		<xsl:with-param name='toconvert' select='substring-after($s," ")' />
		</xsl:call-template>
	</xsl:when>
	<xsl:otherwise>
		<xsl:value-of select='$s'/>
	</xsl:otherwise>
</xsl:choose>bv
</xsl:if>
</xsl:template>

Basically what it does is to check if the string to covert has characters (end condition for recursion), and if it has characters then it breaks the string in two parts: the first letter and the rest of the string, converts the first letter to upper case and checks if the rest of the string contains spaces (word separators), if it contains spaces then it outputs the rest of the current word (substring-before($s," ")) and calls the convertpropercase template with the rest of the string ('substring-after($s," ")') to convert the rest of the words to proper case.

Procedural Programming in XSLT 

If you were wondering what the xsl:call-template element does, it allows one to use functions in XSLT, where basically you can call a template from anywhere in the XSLT.  You pass the template parameters, like you do in functions.  Instead of the template having a matching element, you give it a name. (<xsl:template name='convertpropercase'>)

Declaring the template:

  <xsl:template name='convertcase'>
    <xsl:param name='toconvert' />
    <xsl:param name='conversion' />
   ....
  </xsl:template>

Calling the template:

	<xsl:call-template name='convertcase'>
		<xsl:with-param name='toconvert' select='$f' />
		<xsl:with-param name='conversion'>upper</xsl:with-param>
	</xsl:call-template>
View the Source

 

2005년 10월 13일 목요일

[펌] XML을 PDF문서로 만들기

 

XML을 PDF문서로 만들기

Summary :
XML을 처음 접할 때 XML의 장점으로 많이 듣는 것중의 하나는 XML로 작성된 문서를 다양한 문서로 변환하기 쉽다는 것이다. XML로 작성된 문서는 적용하는 XSL에 따라 XML, HTML, WML, PDF등 다양한 형태의 문서로 변환할 수 있다. 하지만 국내 현실에서 XML공부할 때 HTML로만의 변환에만 치우치는 경향이 있어 XML을 PDF로 변환하는 방법에 대하여 알아본다. XML을 PDF로 변환할 때 해결하기 힘든 부분이 한글이 깨지는 문제이다. 따라서 한글문제에 대한 해결책도 제시한다.

  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문서를 PDF로 변환하기

이번 강좌에서 진행할 모든 예제는 위에서 다운받은 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 &#13;
2 &#13;
3 &#13;
4 &#13;
5 &#13;
</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문서는 다음과 같다.

bgimage.pdf문서  : fopToPDF.jsp에 의하여 생성된 bgimage.pdf문서

  XML과 XSL을 이용하여 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문서는 다음과 같다.

glossary.pdf문서  : xmlToPDF.jsp에 의하여 생성된 glossary.pdf문서

  한글로 작성된 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파일은 다음과 같다.

hangulglossary.pdf문서  : hangulXMLToPDF.jsp에 의하여 생성된 hangulglossary.pdf문서

  결론

지금까지 FOP, XML을 이용하여 PDF문서를 작성하는 방법에 대하여 살펴보았다. PDF를 작성할 때 가장 큰 걸림돌이였던 한글문제에 대해서도 새로운 폰트를 추가하여 해결할 수 있었다. 위에서 사용한 모든 소스는 fopxml.war로 제공될 것이다. 이 파일을 tomcat\webapps\에 넣으면 지금까지의 테스트를 손쉽게 해볼 수 있을 것이다. 수정할 부분은 JSP파일에서 fo와 xml파일들을 경로를 수정해 주기만 하면 된다.
앞으로 FOP강좌에서는 클래스 내에서 위 작업을 손쉽게 할 수 있도록 수정해볼 것이다. 또한 PDF에서 SVG파일을 사용하는 방법에 대하여도 살펴볼 것이다. 만약 여유가 된다면 FOP에서 사용하는 태그에 대하여도 살펴볼 생각이다.

앞으로도 많은 개발자들의 관심이 있기를 바란다. 모든 질문은 XML Q&A란을 이용해주기 바란다. 많은 개발자들과의 정보공유를 위하여 꼭 게시판을 이용해 주기바란다. E-mail을 통한 질문은 삼가해 주기를 바란다.

참고 자료  :