3. Broken Format


3.4. Body


Body는 오브젝트들로 구성되어 있다고 앞 장에서 설명하였다. 이 영역은 PDF 파일의 대부분을 차지할 만큼 방대하다. 모든 내용을 다루기는 어렵고, 극히 일부에 대해서만 다루고자 한다.

 

가장 먼저 다룰 필드는 스트림의 길이를 나타내는 /Length 키이다. PDF의 오브젝트는 아래 그림과 같이 오브젝트의 속성에서 스트림의 압축방식(/Filter/FlateDecode)과 길이(/Length 145)를 나타낸다

여기서 이 /Length 키는 PDF Reader가 참고하는 부분이 아니다. 이 키에 대한 값이 실제 stream의 길이와 다르더라도 문서는 정상적으로 읽히기 때문이다. , /Length 키와는 무관하게 PDF Reader stream 키워드와 endstream 키워드로만 스트림을 읽어들인다.


stream endstream 키워드 사이에 “endstream”이 존재하는 Fake Keyword가 삽입되는 케이스도 있다. 이 경우 어떻게 되는가에 대해서는 아래 URL을 참고하기 바란다.

http://cerbero-blog.com/?p=1612


각종 키워드가 아래 그림과 같이 오브젝트 사이에 마구잡이로 존재해도 아무 문제 없다.

 PDF는 위치 또는 길이를 나타내는 필드를 신뢰하지 않는다. , 키워드에 따라서 이후에 반드시 해당 값이 존재해야 한다는 제약이 없다. 이러한 것은 PDF Reader에서 이상한 포맷이라고 간주하고, 복구해 줄 수도 있다(포맷이 크게 변할 수 있다).

PDF 문서를 파싱할 때에는 Header - Trailer - Cross-Reference Table - Body 순으로 하기에는 부적합하다. 따라서 문서의 첫 부분부터 순차적으로 내용을 읽어나가야 한다.

 

귀찮아서 점점 대충 쓰고있다.



Posted by bong9
,

3. Broken Format


3.3. Cross-Reference Table


Cross-Reference Table xref 키워드로 시작되는 아래 그림과 같은 구조로 이루어져있다.

이 샘플에서 Line 146의 오브젝트 시작 번호(0)와 개수(20)가 신뢰성 있는 것인지, Line 147부터 Line 166까지 각각의 엔트리에 있는 Offset과 사용 여부 필드들이 신뢰성 있는지를 알아볼 것이다. 다음과 같은 의문에 대해 테스트를 해 본다.


1. Cross-Reference Table이 없어도 문서 파일이 열리는가?

2. 오브젝트 시작 번호가 실제 Body의 정보와 다르더라도 문서 파일이 열리는가?

3. 오브젝트의 개수가 실제 Body의 정보와 다르더라도 문서 파일이 열리는가?

4. 오브젝트의 개수가 실제 Entry 개수와 일치하지 않더라도 문서 파일이 열리는가?

5. Entry의 오프셋 정보가 실제 오브젝트의 위치를 가리키지 않더라도 문서 파일이 열리는가?

6. Entry의 오브젝트 사용 여부 필드가 문서 파일을 읽는 데에 영향을 미치는가?


1번 의문에 대해서는 위와 같이 Cross-Reference Table을 전부 제거해버리고 문서를 열어보면 된다. Trailer와 마찬가지로 문서는 너무나 잘 열린다.


오브젝트 시작 번호를 50으로, 오브젝트 개수를 4로 변경하고 실행해본다. 이것으로 2, 3, 4번의 답을 얻을 수 있다.


Entry의 오프셋 정보를 바꾸고, 사용 안함(f)로 변경한 후 테스트한다. 이렇게 5, 6번의 의문점을 풀 수 있다.


모든 테스트에서 문서는 너무나 잘 열렸다. 결론적으로 Cross-Reference Table은 문서가 열리는 것에 대해서 전혀 관계가 없음으로 확인되었다.



Posted by bong9
,

3. Broken Format


3.2. Trailer


PDF 파일의 끝에는 Trailer가 존재한다고 앞 장의 구조 설명에서 언급하였다.



위 그림에서의 샘플은 Header - Body - XrefTable - Trailer - XrefTable - Trailer 순으로 구성되어 있다. 선택한 영역인 Trailer 영역을 전부 삭제하더라도, 파일은 문제 없이 실행된다.

여기서 몇 가지 가설을 세우고 각각의 가설을 증명해 볼 수 있다.


가설 1. 두 번째 Cross-reference Table이 무시되고, 첫 번째 Trailer가 인식된다.


두 번째 Cross-reference Table 영역을 지우고 실행시켜 본 결과, 문제없이 실행됨을 확인하였다. 이것으로 두 번째 Cross-reference Table이 무시된다는 것은 어렴풋이 알 수 있겠지만, 첫 번째 Trailer가 인식된다는 것을 증명하지는 않는다.


가설 2. Trailer가 없어도 PDF Reader는 문서를 문제없이 읽어들인다.


첫 번째 Trailer부터 파일의 끝까지 지우고 실행해 본 결과, 실행 오류가 발생함을 확인하였다. 정말 PDF Reader가 Trailer를 정상적으로 인식 하는건가에 대한 의문을 다시 품고, 다른 가설을 세워보도록 한다.


가설 3. Trailer 내의 요소들이 반드시 전부 존재해야 한다.



샘플의 두 번째 XrefTable Trailer를 제거한 후 테스트를 진행해 본다. Trailer의 요소는 trailer 키워드, Dictionary, startxref 키워드, xref table의 위치값, EOF marker 5개가 존재한다. 각각의 요소들이 신뢰성이 있는가에 대해 알아보도록 한다.




startxref 키워드와, xref table의 위치값, EOF markertrailer에서 없더라도 정상적으로 문서가 열리는 것으로 확인되었다. 하지만 PDF Reader는 열린 문서를 닫을 때 다음과 같은 메시지를 출력한다.




Adobe Reader로부터 저장 된 PDF 파일은 Trailer가 없는 모습이고, 상당히 많은 구조가 변경되었다. 심지어 새로 저장 된 파일에는 trailer 키워드가 존재하지 않는다.




또 다른 특이사항으로는, Trailer가 없음에도 startxref 키워드가 존재한다는 것이다.





여기까지의 테스트로 다음과 같은 사실을 알 수 있다.


1. startxref Trailer에 종속적이지 않다.

2. Trailer가 없는 PDF 파일도 존재하고, 이러한 파일이 PDF Reader에 의해 읽혀질 수 있다.

3. startxref 키워드와, xref table의 위치값, EOF marker는 무시될 수 있다.

Posted by bong9
,

3. Broken Format


3.1. File Header


PDF Header“%PDF-1.7”과 같이 버전 정보를 포함한 시그니처로 구성되어있다. 여기서 확인해야 할 점은 PDF의 유효한 시그니처이다. 몇 번의 테스트를 해 본 결과, 앞의 5 Bytes“%PDF-”가 실제 PDF Reader가 체크하는 것으로 확인되었다.


Test 환경 : Adobe Reader X



또한 이 Header“%”로 시작한다. 이는 PDF Data Type Comment에 해당한다. Comment“%”로 시작해서 End-of-Line으로 끝난다고 되어있다. 그렇다면 과연 이 Header 또한 Comment처럼 처리될 것인지 의문을 가질 수 있다.


“%PDF-“ 이후의 값을 End-of-Line으로 변경하고, 8번째 문자를 특정 값으로 남겨놓자. 문서를 열어보면 이 의문점은 쉽게 풀린다.




여기서 두 가지의 사실을 알 수 있다.


1. 유효 Signature“%PDF-“이다.

2. Header Signature 체크 후 Comment로 처리된다.






Posted by bong9
,

2. PDF Data Type


2.1. PDF Chracter Set


PDF Reader는 파일을 읽어들일 때 특정 규칙에 의거하여 읽어들인다. 각각의 문자마다 의미가 있고, 그에 따른 처리 방식이 달라진다. 여기서는 그 구분 방법에 대해 설명하도록 한다.


PDF에서 특수한 문자는 White-space, Delimiter가 있다. White-space는 흔히 우리가 아는 공백문자이며, Delimiter는 이후 올 문자들을 구분짓기 위한 구분 문자이다. 해당 특수 문자의 의미는 아래 표와 같다.




2.2. Comments


CommentsPDF에서 주석을 의미하는 것으로, “%” 구분자로 시작하여 End-of-Line으로 끝난다. Comments는 지시어 사이, 파일의 처음 또는 끝 등에 존재할 수 있다.




2.3. Boolean


Boolean PDF의 오브젝트 내에서 참(“true”)과 거짓(“false”)을 나타낸다.




2.4. Numeric


Numeric PDF에서 숫자를 표현 할 때 사용된다. 크게 Integer objectsReal objects로 구분되며, 각각의 예는 아래 그림과 같다.





2.5. Literal String


Literal String PDF에서 문자열을 나타낼 때 사용된다. “(“로 문자열의 시작을, “)”로 문자열의 끝을 알린다.



 Literal String 내에서는 개별적인 “(“, “)” 또는 “\”를 허용하지 않는다. 위의 그림을 보면 알 수 있겠지만 Literal String 내의 “(“, “)”는 쌍으로 이루어져 있을 때에는 특별한 처리 없이 문자열로 인식된다. 개별적인 “(“ 또는 “)” 혹은 특수한 문자들은 “\”를 이용해 시퀀스 문자로 표현할 수 있다.




2.6. Hexadecimal Strings


Hexadecimal String“<”“>” 안의 0-9, A-F로 이루어진 2바이트의 데이터가 하나의 Hex data로 인식된다. 이 문자열 안의 White-space는 무시된다.


 


2.7. Name


PDF에서 Name“/” 이후의 문자열로, 0x21에서 0x7E까지의 문자로 구성된다. 이 자료형 내에서 “#”은 이후 2바이트의 문자가 하나의 Hex 값으로 취급하는데, 아래의 표를 보면 이해가 쉬울 것이다.




2.8. Array


Array PDF에서 “[“로 시작하여 “]”로 끝나는 배열을 의미한다. 배열 내 각 요소들은 Array를 포함한 Name, String, Number, Dictionary PDF의 모든 자료형을 가질 수 있다. 요소가 없는 빈 Array도 존재한다.




2.9. Dictionary


Dictionary“<<”“>>” 내에서 Key-Value의 쌍으로 구성된다. Key는 반드시 자료형이 Name이어야 하고, ValueDictionary를 포함 한 모든 자료형을 가질 수 있다. 요소가 없는 빈 Dictionary도 존재한다.




2.10. Indirect


Indirect“n1 n2 R”과 같이 표현되며 Referencing 하는 오브젝트를 표현한다.




2.11. Stream


Stream은 오브젝트 내에서 “stream” 키워드와 “endstream” 키워드 사이에 존재한다.



Stream은 해당 오브젝트의 Dictionary에서 몇 가지의 속성을 지시하는데, 기본적으로 /LengthStream의 길이를 나타내주어야 한다.





여기까지 PDF의 Data Type을 살펴보았다. 다음 장에는 PDF의 Spec과 맞지 않는 포맷에 대해 알아볼 것이다. 즉, PDF 포맷 공개 문서에는 나와있지 않은 실제 PDF Reader가 읽어들이는 방법에 대한 내용을 다루도록 하겠다.





참고 자료

PDF32000_2008.pdf ( Adobe Systems PDF 포맷 공개 문서 )


Posted by bong9
,

1. PDF File Format


1.1. PDF File Format


여기서는 Adobe Systems에서 공개한 PDF 파일 포맷에 대해 설명하도록 하겠다.




PDF 파일 포맷은 크게 Header, Body, Cross-reference Table, File Trailer로 총 4가지 영역으로 구분된다. 각각의 역할은 아래와 같다.


Header : 8바이트로, PDF의 시그니처와 PDF 문서의 버전 정보를 포함한다.

Body : 실제 문서의 정보들을 포함하는 오브젝트들로 구성되고, 이 오브젝트들은 트리 형태로 링크되어있다.

Cross-reference Table : 각 오브젝트들을 참조할 때 사용되는 테이블로, 오브젝트의 사용 여부와 번호 등이 저장된다.

File Trailer : File Body에 존재하는 오브젝트 중 루트 오브젝트가 무엇인지, Cross-reference Table이 어디에 있는지 기재된다.



1.2. File Header


PDF는 대부분 문자열로 구성되어있어 다른 파일포맷보다는 보기에 조금 수월하다. (파서를 제작할 때는 제외한다.) PDF Header는 총 8바이트로, PDF의 시그니처와 문서 버전정보를 포함한다고 앞서 말했다. 위 그림에서 “1.4” PDF의 문서 버전 정보를 의미한다. 각 시그니처와 버전정보는 아래 도표와 같다.




1.3. File Trailer


Trailer는 파일의 끝에 위치하여 Root 오브젝트의 위치와 Cross-reference Table의 위치를 표시한다Adobe Systems에서 배포한 문서를 보면, 맨 앞의 헤더를 읽은 후 파일의 끝의 Trailer를 확인하도록 포맷이 설계되어 있다. 이 Trailer는 위의 그림과 같은 모습을 하고 있고, “trailer”라는 지시어로 시작하여 “%EOF”로 끝난다. 



line 80“<<” ~ “>>”는 현재 오브젝트의 속성을 표기한다. Trailer는 여기에 다음의 키 지정이 가능하다.


/Size (Integer) : Cross-reference Table의 항목 수

/Prev (Integer) : 이전 Cross-reference Table의 위치

/Root (Dictionary) : Root 오브젝트의 오브젝트 넘버를 표시


그림을 기준으로, /Root라는 이름의 속성은 Root 오브젝트를 의미하며, 여기서는 1번 오브젝트를 레퍼런싱하고 있다. /Size 속성은 오브젝트들의 참조 테이블 즉, Cross-reference Table의 엔트리 개수를 나타낸다.


여기서 “<<” ~ “>>” PDF의 자료형 중에서 Dictionary라고 표현한다. Dictionary는 키와 값으로 이루어진 데이터들을 포함하며, 위의 그림에서는 /Root /Size 키가 된다. 이러한 키들은 PDF에서 Name이라는 자료형이어야 하며, Name 자료형임을 구분할 수 있는 구분문자가 “/”가 된다.


/Root의 값인 1 0 R에서 R Reference의 약자로, 오브젝트를 참조한다는 의미를 가지고 있다. 이를 PDF에서는 Indirect Object 자료형이라고 한다. /Size의 값인 10은 그저 10진수로, Integer 자료형이다.


Trailer startxref라는 키 지정이 가능하다(line 81). 이는 Cross-reference Table의 위치값을 표현해준다. 이후에 오는 값인 8061이 파일에서 Cross-reference Table의 시작 오프셋이 된다.



1.4. Cross-reference Table


Cross-reference Table Body에 존재하는 오브젝트들의 위치값과 사용 여부를 기록한다. 이러한 참조 테이블을 순서대로 기록하여, PDF Reader는 이를 이용해 각 오브젝트의 위치로 이동할 수 있다. Adobe Systems의 문서를 보면 이 테이블의 각 항목은 공백을 포함하여 총 20바이트의 크기를 갖도록 되어있다.


위 그림을 보면 xref라는 키워드가 존재한다. 이는 Cross-reference Table의 시작임을 알리며, line 68부터 실제 Table이 기재된다


line 68을 공백으로 구분하여 첫 번째 숫자는 오브젝트의 시작 번호, 두번째 숫자는 엔트리의 개수를 의미한다. 여기서는 0번 오브젝트부터 9번 오브젝트까지 총 10개의 오브젝트에 대한 위치값과 사용 여부가 line 69부터 line 78까지 표시된다


각 엔트리 또한 공백으로 구분되어 첫 번째 숫자는 해당 오브젝트의 위치값, 두 번째 숫자는 생성 숫자(Adobe Systems의 문서에서는 5-digit generation number로 표현됨), 세번째의 문자는 해당 오브젝트의 사용 여부를 나타낸다. 오브젝트의 사용 여부는 “f”(free entry) 또는 “n”(in-use entry)으로 표기된다. 위의 테이블을 해석하면 아래와 같다.


line 68 : 오브젝트 시작 번호, 10 - 엔트리 개수

line 69 : 0번 오브젝트. offset : 0. 사용되지 않음

line 70 : 1번 오브젝트. offset : 17. 사용됨

line 71 : 2번 오브젝트. offset : 109. 사용됨

line 72 : 3번 오브젝트. offset : 169. 사용됨

line 73 : 4번 오브젝트. offset : 267. 사용됨


Cross-reference Table은 다음과 같은 형태로도 존재할 수 있다.

xref

0 2

0000000000 65535 f // 0번 오브젝트. offset : 0. 사용되지 않음

0000000017 00000 n // 1번 오브젝트. offset : 17. 사용됨

2 1

0000000109 00000 n // 2번 오브젝트. offset : 109. 사용됨

3 2

0000000169 00000 n // 3번 오브젝트. offset : 169. 사용됨

0000000267 00000 n // 4번 오브젝트. offset : 267. 사용됨


1.5. File Body


Body는 파일의 거의 대부분을 차지하고, 오브젝트들로 구성되어 있다. 오브젝트의 시작은 “n1 n2 obj”로 시작하고, “endobj”로 끝나는 것으로 각 오브젝트의 영역이 구분된다. 각 오브젝트는 자신의 성격을 나타내는 정보들을 가지고 참조하는 오브젝트에 대해서도 알려준다. 또한 내부에 stream 영역에 그림, 메모 등과 같은 데이터를 포함할 수 있다.



위의 그림에서 line 3에는 1 0 obj라는 1번 오브젝트의 시작을 알리는 지시어가 존재한다.

1번 오브젝트를 해석하자면 아래와 같다.


이 오브젝트의 타입은 Catalog이다.

페이지 레이아웃은 싱글페이지.

페이지들의 대한 내용은 2번 오브젝트를 참조할 것

문서 열람시의 액션은 4번 오브젝트를 참조할 것

 

여기까지가 기본적인 PDF의 파일 포맷이다. 이를 이용하여 다음 그림과 같이 PDF의 전체적인 구조를 파악할 수 있다.




1.6. Incremental Updates


PDF 파일은 아래 그림과 같이 파일의 재 작성을 고려하여 업데이트되는 데이터를 파일 후반부에 추가하도록 설계되었다. 따라서 한 파일에 Body, Cross-reference Table, Trailer는 여러 개 존재할 수 있다.


이번 장에서 PDF 파일 포맷의 전체적인 구조를 간략히 설명했다. 다음 장에서는 파일의 대부분을 구성하는 오브젝트에서의 Data Type에 대해 다루도록 하겠다.





참고 자료

http://www.nurilab.co.kr/?p=357 ( 누리랩 )

PDF32000_2008.pdf ( Adobe Systems의 PDF 포맷 공개 문서 )

Posted by bong9
,