본문 바로가기
컴퓨터/MFC_API

CRichEditCtrl 에서 유니코드 표시

by adnoctum 2014. 5. 2.




   지금 RTF 파일에 글자(한문)를 저장해 놓은 후 읽어들여서 CRichEditCtrl instance 에 표시를 해주고 있는데, 이게 종종 글자가 깨진 것이 보인다. 이 경우, RTF 파일을 읽을 때 그 부분의 format 까지 같이 읽어서 저장해 놓은 후 표시할 때 그 format 을 사용하는 방법으로 우회하고 있다. 보다 정확한 방법은 사실 이론상으로 하기 힘든 듯 한데 확실하진 않다. 



   RTF 파일은 글자 개별적으로 code-page 를 지정할 수 있게끔 되어 있다. 따라서 한 RTF 파일이라 하더라도 각 글자마다 서로 다른 code-page를 사용할 수 있다. CRichEditCtrl-derived view 를 이용해서 무조건 글자를 출력해버리면 code-page 가 맞지 않아서 글자가 깨져 나올 수 있다. 즉, ReplaceSel 함수를 이용해서 CString 형 변수를 rich edit control 에 써 넣을 때, CString 형 변수는 제대로 되어 있어도 실제로 쓰여 진 글자는 깨질 수 있다. 다음과 같다. 





위처럼 글자가 깨져 나왔다. 하지만 실제 변수에 들어 있는 값은




위처럼 정상이다. 그럼 이제 각 내용을 쓰면서 character format 을 원래의 포멧 그대로 지정해 보자. 코드는 다음과 같다. 우선 RTF 파일을 읽어 들이는 부분은, 


bool load_one_column_item_from_rtf(CString FileName, std::map<CString, CHARFORMAT2>* dest) { CRect rect;  AfxGetMainWnd()->GetClientRect(&rect);  CRichEditCtrl rec;  rec.Create(WS_CHILD | ES_MULTILINE, rect, AfxGetMainWnd(), 1010202);  rec.SetTargetDevice(NULL, 1);  CHARFORMAT2 cf;  LoadRTFFromFile(FileName, &rec);  int nLineCount = rec.GetLineCount();  int i = 0;  for(= 0; i<nLineCount; i++){ int start_pos = rec.LineIndex(i);  int line_length = rec.LineLength(start_pos);  rec.SetSel(start_pos, start_pos + line_length);  rec.GetSelectionCharFormat(cf);  CString sel_text = rec.GetSelText();  sel_text.Trim();  if(sel_text == _T("")) continue;  if(sel_text[0] == _T('#')) continue;  dest->insert(std::make_pair<CString,CHARFORMAT2>(sel_text, cf));  } return true; }



(현재 암묵적 가정은 한 줄에 사용된 모든 글자는 동일 code-page 라는 것이다. 이 가정이 통하지 않는 경우에는 보다 세밀하게 code-page 를 저장해야 한다)


위처럼 각 string 을 저장할 때 그 string 을 표시하기 위해 사용되었던 format 까지 몽땅 저장한다. CHARFORMAT2 중 code-page 를 지정하기 위해 사용되는 것은 일부일테지만 나는 다른 member 를 그대로 가져와도 상관없기 때문에 CHARFORMAT2 전체를 저장했다. 그 후 실제로 내용을 쓰는 부분을 보면 다음과 같다. 




	std::map<CString,CHARFORMAT2> herbs; 
	load_one_column_item_from_rtf(_T("tagging.rtf"), &herbs); 
	int index = 0; 
	std::map<CString, CHARFORMAT2>::const_iterator pos;
	for(pos = herbs.begin(), index = 1; pos != herbs.end(); pos++, index++){
		CString str; 
		str.Format(_T("약재\t%d\t%s\n"), index, pos->first); 
		rec.ReplaceSel(str); 
		int line_index = rec.LineIndex(index-1); 
		int line_length = rec.LineLength(line_index); 
		rec.SetSel(line_index, line_index + line_length); 
		CHARFORMAT2 cf = pos->second;
		rec.SetSelectionCharFormat(cf);  // format 지정 (code-page 도 포함됨).
		rec.SetSel(line_index + line_length, line_index + line_length); 
	}
 




위처럼 해서 일단 문제를 우회하기는 했다. 


하지만 위는 정확한 해결책은 아닐 것이다. 기본적으로 내가 받은 unicode 값이 어느 code-page 인지를 알아 내는 방법은 없을 것 같다. 그렇기 때문에 code-page 는 무조건 외부에서 받아야 하며 주어진 unicode 값이 어느 code-page 인지를 알아낼 수는 없다. RTF 파일엔 이게 다 지정이 되서 저장이 되는데, CString 은 그것을 적당히 처리하여 글자가 깨지지 않고 아주 잘 표현해 준다. 


위에선 map 에 CString 과 CHARFORMAT2 를 넣고 있는데 이 역시 내가 지금 만나는 상황이 아닌 경우 충분히 문제가 발생할 수 있고, 따라서 자신이 속한 문제의 상황에 따라 잘 조절해야 할 것이다. 





하여튼... 유니 코드는 너무나 짜증나. 지금 한자가 아주 많이 들어 간 문서를 처리하고 있는데, 한자+한글이 섞여 있는데다 한문 자체가 워낙 많아서 한 코드 페이지로 표시가 안된다. RTF 파일을 CRichEditCtrl 로 읽어 들여서 이 안에서 처리하다가 너무 짜증이 나고 파일 크기가 2~3MB 만 되어도 얘가 버벅대서 나중엔 그냥 RTF 자체를 직접 건드려서 작업했다. byte 단위로 작업하는 일은 C/C++ 을 쓸 경우 아주 흔한 일이라 전혀 어렵지 않았고, 나중엔, ㅋㅋㅋ, 아예 text file 이라 간주하고 python 으로도 RTF 파일을 처리하고 막 그랬다, ㅋ.