이번 포스팅에서는 한글문서가 어떤형식으로 파일구조를 갖고 있고 

어떻게 바이트단위로 박혀있는지 확인 해보는 시간을 갖기 위해 글을 써보았다.



한글 문서는 Compound File (OLE2) 구조를 갖는다.

스토리지와 스트림 형태를 띄고 있는데, 스토리지는 폴더, 스트림은 파일이라고 이해하면 쉽다.

Root 밑에는 여러개의 스토리지와 스트림이 존재한다. 

아래의 표는 hwp 파일이 갖고있는 구조를 표로 표현한것이다.



 

 이름

길이 

레코드 

압축 / 압축화 

 파일 인식 정보

FileHeader (Stream)

고정 

 

 

 문서 정보

DocInfo  (Stream)

고정 

true 

true 

본문 

BodyText (Storage)

- Section0   (Stream)

가변 

true 

true 

문서요약 

\005HwpSummaryInformation  (Stream) 

고정 


 

바이너리 데이타 

BinData  (Storage)

- BinaryData0   (Stream)

- ...   (Stream)

가변 

 

true 

미리보기 텍스트 

PrvText  (Stream)

고정 

 

 

미리보기 이미지 

PrvImage  (Stream)

가변 

 

 

문서 옵션 

DocOptioin  (Storage)

- _LnkDoc  (Stream)

- DrmLicense  (Stream)

- ...

가변 

 

 

스트립트 

Script  (Storage)

- DefaultScript  (Stream)

- JScriptVersion  (Stream)

- ...

가변 

 

 

 XML 템플릿

XML Template (Storage)

- Schema   (Stream)

- Instance  (Stream)

- ...

가변 

 

 

문서 이력 관리 

DocHistroy  (Storage)

- VersionLog0  (Stream)

- VersionLog1  (Stream)

- ...

가변 

true 

 

true 



(ssviewer로 본 한글파일 내부의 구조)

각 스토리지와 스트림의 정보는 한글과 컴퓨터에서 제공하는 문서를 참고하면 이해하기 쉽다.



간단하게 파일 인식 정보를 갖고있는 FileHeaer 스트림을 뜯어보면 아래와 같은 구조를 갖고 있다.




FileHeaer은 256바이트의 고정된 길이를 갖는 스트림이다.

사진에 첨부한 FileHeader 스트림의 Binary값을 확인해보면 

초기 32바이트에는 한글 문서 형식을 알리는 시그니처 문구가 박혀있는것을 확인할 수 있다.


그다음 4바이트 크기로 한글 문서의 버전을 확인 할 수 있다. 

바이너리 뷰어로 해당 스트림을 열어보면 5.0.3.0 버전이 찍힌걸 볼 수있다.


그다음 4바이트는 파일헤더의 속성을 갖는 비트들이다. 0비트부터 11비트는 각각의 문서 속성 정보를 갖고있다.

그 이후 나머지 216바이트는 예약 바이트들로 0의 값으로 초기화되어 있는 형태를 확인할 수 있다.




한글 문서중 제일 파악하기가 쉬운게 파일헤더 스트림이다.

고정 길이를 갖고 있기에 256바이트 중에 

처음 32바이트는 시그니처, 

그다음 4바이트는 파일의 버전, 

그다음 4비트는 속성 정보,

나머지 216바이트는 예약비트로 파싱할때 비교적 수월하다.

암호화도 안되있기 때문에 그냥 가져와 바이트단위로 짜르면 한글문서의 정보를 가져올 수 있다.





다른 스트림도 이러면 좋겠지만 가변적인 길이를 갖는 스트림도 많고 압축/암호화도 걸린게 많기 때문에 

다른 스트림을 파싱하거나 렌더링할때에는 쉽게 되지는 않을꺼같다.




몇몇 스트림들은 레코드형식(헤더와 데이터의 결합)으로 구성되어있기도 하며, 

MSDN의 프로퍼티 셋 형식을 취한 스트림도 있다.




대표적인 예로 \005HwpSummaryInformation 스트림이 MSDN의 프로퍼티셋 형식을 갖고 있다.

Summary Information에 대한 자세한 설명은 MSDN을 참고

The Summary Information Property Set

The DocumentSummaryInformation and UserDefined Property Set

(한글 공식 문서의 설명)


SumamryInformation 은 MSDN의 프로퍼티셋 형식을 따른다.

해당 정보는 아래의 URL에 설명되어있다.


https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa380376%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396


HwpSummaryInformation 정보를 가져오기 위해서는 바이트 단위로 짜르는것 보다는

Apache POI API에서 제공하는 프로퍼티 ID단위로 값을 가져오면 된다.

이 부분은 추 후 포스팅을 통해 값을 가져와보도록 하겠다.









마지막으로 설명하는 내용은 레코드 형식의 데이터다.


(한글의 데이터 레코드 구조)


논리적으로 연관된 데이터들을 헤더 정보와 함께 저장하는 방식을 데이터 레코드라고 한다.

레코드 구조를 가지는 스트림은 연속된 여러 개의 레코드로 구성된다. 데이터 레코드는 헤더와 데이터로 구성되며 각 헤더 정보를 활용하여 전체 논리적 구조를 생성하게 된다.

레코드의 헤더에는 데이터 확장에 대비한 정보를 가지고 있다. 따라서 이후에 ᄒᆞᆫ글의 기능이 확장되어 레코드에 데이터가 추가되는 경우에도 하위 버전의 ᄒᆞᆫ글이 상위 버전의 ᄒᆞᆫ글 문서를 읽을 수 있도록 하위 호환성이 보장된다.



Tag ID : 레코드가 나타내는 데이터의 종류를 나타내는 태그이다. Tag ID에는 10 비트가 사용되므로 0x000 - 0x3FF까지 가능하다.

0x000 - 0x00F = 일반 레코드 태그가 아닌 특별한 용도로 사용한다.

0x010 - 0x1FF = 글에 의해 내부용으로 예약된 영역(HWPTAG_BEGIN = 0x010)

0x200 - 0x3FF = 외부 어플리케이션이 사용할 수 있는 영역


Level : 대부분 하나의 오브젝트는 여러 개의 레코드로 구성되는 것이 일반적이기 때문에 하나의 레코드가 아닌 "논리적으로 연관된 연속된 레코드"라는 개념이 필요하다. 레벨은 이와 같이 연관된 레코드의 논리적인 묶음을 표현하기 위한 정보이다. 스트림을 구성하는 모든 레코드는 계층 구조로 표현할 수 있는데, 레벨은 바로 이 계층 구조에서의 depth를 나타낸다.


Size : 데이터 영역의 길이를 바이트 단위로 나타낸다. 12개의 비트가 모두 1일 때는 데이터 영역의 길이가 4095 바이트 이상인 경우로, 이때는 레코드 헤더에 연이어 길이를 나타내는 DWORD가 추가된다. , 4095 바이트 이상의 데이터일 때 레코드는 다음과 같이 표현된다.

(한글 문서파일형식에서 발췌)



대표적으로 DocInfo스트림과 Body 스토리지 내부의 Section 스트림에서 데이터 레코드형식을 취하고 있다.

TagID의 0x010에 HWP TAG 아이디를 넣음으로써 해당 레코드가 어떤 데이터를 갖고 있는지 구분할 수 있다.


현제 필자는 HwpLib 라이브러리를 통해 한글문서를 파싱, 렌더링 하는 부분을 분석중이다.

바이너리 데이터를 읽고 쓰는것이 익숙하지 않아 하루에도 머리가 여러번 쪼개지는 고통을 받고 있다.ㅋㅋ ㅜㅜ

빨리 이 과제를 마무리 짓고 싶다. ㅜㅜ




















회사 수습 연구원으로 진행하게될 과제이다.

Java로 .hwp파일을 파싱 후 렌더링하기까지가 숙제(과제)이다. 


처음에는 low레벨까지 내려가 binary형태에서 부터 파싱을 해야 하나 싶어서 걱정이 많았는데

자비로우신 팀장님께서 결과만 나오면 일단 된다고 하셨기에 갓구글님께 도움을 요청했다.


http://www.hancom.com/etc/hwpDownload.do 

(한글 문서 파일 구조 5.0 도큐먼트)


처음에 70페이지 가까이된 한글 문서 파일 구조를 던져주실때 식겁했는데

다행스럽게도 


https://github.com/neolord0/hwplib

누군가가 만들어주신 라이브러리가 존재했다.


한글과 컴퓨터(한컴)에서 만든 워드프로세서 "한글"의 파일에 대한 라이브러리입니다.

본 라이브러리는 JAVA로 구현되었으며, 한글 파일의 하부 구조인 Microsoft Compound File의 부분은 Apache-POI의 POIFS File System을 사용하여 처리합니다. 본 라이브러리는 한글과컴퓨터의 한글 문서 파일(.hwp) 공개 문서를 참고하여 개발하였습니다. 한컴에서 제공하는 문서(HWP 5.0)는 아래URL에서 받을 수 있습니다.

http://www.hancom.com/etc/hwpDownload.do?gnb0=269&gnb1=271&gnb0=101&gnb1=140



github에 올라온 프로젝트 소개글이다.

처음에 소스 분석을 하겠다고 처음부터 쭉 따라갔는데 왠 아파치 poi 라이브러리가 나오길래 막막했었다.

알고보니 아파치 poi 라이브러니는 마이크로소프트사의 워드 포맷형식을 지원하는 라이브러리였다.


여기서 생긴 의문이 

그렇다면 한글의 .hwp파일은 MicroSoft의 word 형식이랑 같다는것인가?

hwp나 word는 같은 Compound File Binary Format 이였던것이다.

한글은 XML기반으로도 파일이 제공된다고 팀장님이 알려주셨다.

CFBF, OLE라고도 불리는 이 형태는 하단의 블로그에서 자세하게 설명해준다.

http://www.reversenote.info/ole-file-format/



이제 위의 라이브러리를 통해 1차 목표인 한글파일 읽어들여

콘솔창에 띄우는 결과물을 만들어낼 생각이다.

이미 만들어져있기에 거기까진 어렵지 않지만 그냥 가져다가만 쓰면 개인과제에 의미가 없어보여서

최대한 라이브러리를 뜯어보아서 어떤형식으로 .hwp파일을 파싱하는지 공부해보려고 한다.








주어진 리스트가 정렬이 되어있다는 가정하에서는 

이진 검색(binary search)를 사용하는게 매우 효율적인 방법이란다.


소스를 보니 재귀호출을 사용하여 검색이 진행된다.

(JAVA 프로그래밍 면접 이렇게 준비한다 - 73P)



public class BinarySearch {

public static boolean binarySearch(final List<Integer> numbers, Integer value) {

if(numbers==null || numbers.isEmpty()) // 전달받은 numbers 리스트가 null이거나 비워져있는지 확인

return false;

Integer comparsion = numbers.get(numbers.size() / 2); //  리스트 크기 반띵하여 원소 위치의 값을 추출

if(value.equals(comparsion)) {   

return true;

}

if(value<comparsion) {

return binarySearch(numbers.subList(0, numbers.size() / 2), value);

} else {

return binarySearch(numbers.subList(numbers.size() / 2 + 1, numbers.size()), value);

}

}

public static void main(String[] args) {

List<Integer> list = new ArrayList<>();

list.add(1);

list.add(4);

list.add(5);

list.add(9);

list.add(14); // 정렬하기 귀찮아서 삽입할때 순서대로 삽입시켰다.



System.out.println(binarySearch(list, 9)); // 삽입된 리스트와, 찾고하자는 밸류값을 파라메터로 던진다.

}

}


소스가 어렵지 않아 쉽게 이해할 수 있었다.





미세먼지 측정 어플 '공기어때'를 마켓에 정식 출시했습니다


현제 위치와 가까운 대기 측정소에서 보내오는 대기정보를 사용자에게 알려줍니다!


https://play.google.com/store/apps/details?id=com.sh.finddust






마켓에서 '공기어때'를 검색하세요!


간단하게 어플 소개를 해보겠습니다.



(로딩 화면)



(메인 화면)

현제 위치와 가까운 측정소에 보내오는 대기정보를 확인할 수 있습니다.


미세먼지, 초미세먼지 외 여러가지 대기정보를 한눈에 확인!

우측 상단의 종이비행기 버튼을 클릭하시면 카카오톡 친구에게

미세먼지 정보를 공유하실 수 있어요.




(카카오톡 공유 화면)



(위치 검색 화면)



(즐겨찾기 관리)

추가된 즐겨찾기를 삭제할 수 있습니다.

최대 3개의 즐겨찾기 지역을 추가하실 수 있어요





(공기 어때의 미세먼지 측정 기준)

'공기어때'는 WHO의 기준을 가지고 사용자에게 측정 정보를 제공합니다.




(개발자 정보와 데이터 출처)

공기어때는 2명의 개발자가 개발했습니다.

데이터는 환경부 한국환경공단에서 제공하는 데이터를 사용했습니다.



많은 이용과 관심 부탁드립니다.

읽어 주셔서 감사합니다.

java언어 개발시 필요한 jdk를 설치와 환경변수까지 

설정해보는 포스팅을 해보겠습니다.





1. JDK 설치파일 다운받기




오라클 홈페이지로 접속합니다.

http://www.oracle.com/technetwork/java/javase/downloads/index.html






JAVA 9버전이 릴리즈 되며 JDK 9.0.1을 확인 할 수 있내요.

저는 팀원과 개발환경을 맞추기 위해 JDK 1.8 버전으로 진행하기로 했습니다.

원하시는 버전에 해당하는 JDK DOWNLOAD 버튼을 눌러주세요

(저는 1.8 버전으로 진행하겠습니다)


상단의 라이센스사용 동의에 체크를 해주셔야 다운로드가 가능합니다.

설치하시는 컴퓨터의 운영체제에 맞는 버전을 클릭하여 다운로드하세요









2. 설치하기




인스톨 파일을 실행하여 설치를 진행합니다.

설치과정은 어려움이 없으니 천천히 다음 단계로 넘어가면서 진행하세요.






이렇게 설치가 마무리 되었습니다.

이제 환경변수를 설정해볼까요?






3. 환경변수 설정




제어판->시스템 및 보안->시스템

혹은

내 PC 우클릭 -> 속성


시스템창을 연뒤 고급 시스켐 설정을 클릭합니다.






환경변수 버튼을 클릭합니다.





시스템 변수의 '새로 만들기'를 클릭합니다.





변수 이름을 JAVA_HOME

변수 값을 JDK 설치경로로 잡습니다.

JDK를 기본 설정으로 설치하시면 C:\Program Files\Java\jdk1.8.0_151 

(9버전을 받으신분은 실제 디렉토리에 가셔서 버전을 확인하세요)





확인 버튼을 누른뒤 

시스템 다이얼에 Path를 클릭후 '편집' 버튼을 누릅니다.





'새로 만들기' 버튼을 클릭하신 뒤





%JAVA_HOME%\bin을 입력합니다.

jdk 버전업을 하게되더라도

아까 설정한 JAVA_HOME 디렉토리만 바꿔주시면

다른 설정 안건들이셔도 되요.








4. 설치 확인



이로써 JDK 설치가 끝났습니다.

이제 환경변수가 제대로 먹히는지 확인해봐야겠죠?


윈도우 -> cmd를 검색하셔서

커맨드창을 뛰웁니다.


자바 명령어를 통해 설치된 jdk 버전을 확인해봅시다


커맨드창에서

java -version을 입력하면

아래의 화면처럼 버전정보가 뜨게됩니다.

그럼 성공~









+ Recent posts