문자열의 길이를 알 수 있을까요?

OpenGL 한글 (입)출력 라이브러리 "glan"에 대한 포럼입니다.

Moderator: 류광

비회원

문자열의 길이를 알 수 있을까요?

Post by 비회원 »

draw_string() 함수에 입력한 문자열이 윈도우 창에서 얼마의 높이와 너비를 차지하게 될지

알 수 있을까요?
비회원

MSDN

Post by 비회원 »

GetTextExtentPoint32
eoh
Posts: 135
Joined: 2001-07-20 09:00
Location: REAL:DREAM
Contact:

Post by eoh »

답글을 하나 올려주신 분께는 죄송하지만, GLAN에서 GetTextExtendPoint32로 그 출력위치를 알아 낼수는 없을것으로 생각됩니다. 또한 그렇게 의도되지 않았고요.

기본적으로 glanText 는 화면상의 임의의 위치에 글자를 출력하기 위한 객체입니다. 따라서 특정 범위내에서 워드랩같은것은 지원하지 않습니다. glanText로 글자가 출력되는 범위를 추측, 예측하시고자 한다면, 가로축으로는 귀찮은 일을 해주시면 가능하고, 세로축으로는 단 한줄만이 출력됩니다.
  • occupy_height = glans::height [pixel]
라고 보시면 될것입니다. 위의 식은 단순히 속성만을 적어둔것입니다.

가로축 글꼴의 너비를 알고 싶으시다면, 귀찮지만 라이브러리 내부의 글꼴 객체를 수정해서 일부의 맴버 변수에 접근할수 있게 되면, 얻을수 있습니다.

구체적으로는 실제의 glanTextImp 객체의

Code: Select all

in glan_text.h
83:	glanFontSystem* pfont_;
84:	glanGlyphTraits traits_;
맴버 변수와, glanFontSystem객체의

Code: Select all

in glan_fontsystem.h
62:	glanGlyph* bind_( ISqchar ch__ );
맴버 함수를 이용하면 됩니다. 다만, 위의 3가지 모두 private, protected 맴버이므로, 접근성의 개선이 필요로 해집니다. 그에 대해선, 생략합니다. ;ㅁ;

그리고, 마지막으로 이들을 이용하여 유니코드문자 ch 의 가로 너비를 알아낼 수 있습니다.

Code: Select all

glanGlyph* pGlyph = pfont_->bind_( ch );
int advance_x = glanFontImp::glyph_advanceX(*pGlyph, traits_);
물론 pfont_ 와 traits_ 는 glanTextImp 에서 실제로 사용되어지는 값을 기준으로 합니다. 안전을 위해 복사본을 만들어 사용하시거나, ch만을 glanText에 인자로 넘겨 이 과정을 거치는 맴버함수로부터 값을 얻어내는것이 가능하겠지요..


GLAN2를 만들 때는 STL에서 제공하는 스트림의 형태로, 영역을 지정하여 계속 문자열을 입력해 넣을수 있고, 그 영역속에서 자동적으로 스크롤이 되는 객체를 만들었습니다만, GLAN자체의 심플함 유지를 위해 포함시키지 않았었는데.. 그 사이에, 하드의 고장으로 잔해가 남아있질 않군요...;ㅁ;

그리고 줄넘김 문자로 줄을 넘길수 있는 것까지 만들었던 기억이 있어서, 2.0.0에서도 가능하다고 생각하고 있었는데 소스를 살펴보니 지원이 안되는군요... 아마도, 적정한 선만큼 가로로 출력해주면, 높이*glans::line_space 만큼 아래로 내려가 새로이 출력해주어야 할것 같습니다. orz

제가 GLAN2를 만든지 오래되어서.. 확인하면서 쓴 글이지만, 기억이 불분명한 관계로 중간에 틀린 부분이 있을지도 모르겠습니다. 만약 발견되면 알려주세요 ;ㅁ;
비회원

잘 되지가 않습니다.

Post by 비회원 »

void glanTextImp::GetStringSize(const ISuchar* str__, ISfloat* pWidth, ISfloat* pHeight)
{
*pWidth = 0.0f;
*pHeight = 0.0f;

static IS_CHARSET_TRAITS_UNICODE ctrait;
glanGlyph* pGlyph;

ISqchar ch;
while( *str__ != unichar::NUL )
{
str__ += ctrait.read_char( str__, ch );
pGlyph = pfont_->bind_( ch );
int advance_x = glanFontImp::glyph_advanceX(*pGlyph, traits_);
*pWidth += advance_x;
}

*pHeight = value(glans::height);
}

위와 같이 문자열의 길이를 알아내는 함수를 만들었습니다.

그런데, pfont_->bind_() 함수를 호출하는 부분에서 자꾸만 죽습니다.

문자열의 길이가 2 이상일 경우는 저기서 걸리더군요.

또, 테스트 해본 결과 height가 70 이상일 경우는 얻어지는 width가 실제 필요한 너비의

약 95%밖에 나오지 않았습니다.

제가 어느 부분을 수정해야 할까요?

고수의 조언을 기다립니다.
eoh
Posts: 135
Joined: 2001-07-20 09:00
Location: REAL:DREAM
Contact:

Post by eoh »

직접 돌려보려고.. 간만에 압축을 풀고 컴파일을 해보았습니다.
GCC 3 에 들어오면서 버전간에 상당히 변화가 있던탓에.. 이래저래 문제가 있었거나, 오타가 있었더군요 ;ㅁ;
그리고 경로지정도 잔뜩 하면서.. 이래저래 재 컴파일에 성공하였습니다. 간만에 테스트 프로그램을 보니 정(..)겹네요.


여튼, 알려주신 함수코드의 테스트 결과, 문제없이 작동하였습니다. 그리고 그 내용은, glanFontSystem 에서 글자를 출력하기위한 처리와 같기때문에.. 문제가 없다고 판단됩니다. 다만, 글자출력때도 그랬습니다만.. 같은코드를 쓰기에, 같은 조건이 만족되어야 하죠.

간단히 말하면, glanText 가 화면에 글자를 그릴수 있는 상황에서만, 제대로 길이를 판단 할 수 있을거라고 생각됩니다. 2.0.0 에 같이 포함되어 있는 Textdraw 예제에서는 glanInitText() 함수안에서, 글자들의 중간에서 시험해본 결과 길이를 문제없이 돌려주었습니다.


아마도.. glanFontSystem 의 특성도 있을거라 판단됩니다만.. 역시 실행결과를 보니, 일정 크기 이상으로는 실제로 텍스트를 일정크기 이상으로 크게 만들지 않습니다. 작은 크기의 글꼴은 그 크기와 1:1 의 비율로 텍스쳐에 그리지만, 일정한계폭, 아마도 64 나 72 이후정도부터는 그 크기를 계속 이용하여, 그릴때만 확대하여 그려주게 되죠. 그래서, glyph_advanceX() 얻은 크기는, 실제 텍스쳐에 저장이 되는 글꼴 크기로서, 표시되는 크기와는 다를 수 있습니다. 실제로 그려줄땐 그걸 기반으로 확대를 시켜주는거죠.

따라서, 높이를 기준으로 그 확대 비율만큼 곱해주면 될것 같습니다.


끈적이 GLAN 2 에 일단 적용해 보았습니다. 여전히 문제가 생긴다면, 차이점을 살펴보시는것도 좋을듯 합니다.
rotarykim
Posts: 15
Joined: 2006-06-26 10:55

저게 정말 돌아가나요?

Post by rotarykim »

문자열의 길이가 2 이상일 경우는 bind()할 때 어김없이 죽던데...

여긴 데이터를 올릴수 없어서 eoh님의 등록된 메일 계정으로([email protected])

프로젝트를 보내드립니다.

귀찮으시겠지만 확인 부탁드립니다.
eoh
Posts: 135
Joined: 2001-07-20 09:00
Location: REAL:DREAM
Contact:

Post by eoh »

음.. 일단, 착오가 있었습니다.
한번 그려진 글꼴은 3가지 크기로 캐시에 저장이 됩니다. 16, 62, 128 의 크기로요. 보내주신 소스에서는 크기를 제한하는 부분을 주석으로 처리하셔서, 저도 그 부분을 주석처리 해 보았습니다만 바로 죽어버리는군요. 특정크기의 글꼴을 중심으로 사용하신다면, 위의 3가지로 한정되는 코드부분을 원하시는 글꼴크기로 바꾸시는것이 좋을것 같습니다.

Code: Select all

in glan_fontsyste.cpp (GLAN 2.0.2)
68:  if ( c_traits_.height_ <= 20 )		c_traits_.height_ = 16;
69:  else if ( c_traits_.height_ <= 100 )	 c_traits_.height_ = 62;
70:  else					c_traits_.height_ = 128;

233:  if ( c_traits_.height_ <= 20 )		pList = &glyph16_;
234:  else if ( c_traits_.height_ <= 70 )	 pList = &glyph62_;
235:  else					pList = &glyph126_;
전자는 실제로 제한하는 부분이고, 후자는 제한된 크기로 캐시를 결정하는 부분입니다. 캐시 경계를 아예 변수로 지정하려고 하였지만.. 중간에 변수가 바뀌어 버리면 캐쉬가 초기화 된다거나 하는 일을 해야해서 바로 집어 넣기는 힘들것 같군요. 지금 필요하신대로 고쳐 쓰시기 바랍니다.. ;ㅁ;

현재, 알려주신 문제에 대해서 수정이된 버전 2.0.2 을 작업중입니다. 원인은, glanFontSystem 에서 c_traits_ 가 업데이트 되지 않은 상태에서 그 맴버를 사용하고 있었기 때문에 발생한것으로 생각됩니다. 그래서 그 부분을 고쳤고요. 그외에, 크기를 알기위해 글꼴을 미리 바인딩 하는작업을 하는데, 이것으로 인해서 비트맵 글꼴이 확대가 되지 않는 문제도 고쳐졌습니다.

자세한것은 2.0.2 과 비교해주시기 바랍니다.
rotarykim
Posts: 15
Joined: 2006-06-26 10:55

2.0.2 버전이 잘못 올라온거 같습니다.

Post by rotarykim »

get_occupy_size() 함수를 호출할 때 여전히 bind_() 하는 부분에서 죽는데요...

또한 한글 출력시 글자가 깨집니다.

확인 부탁드립니다.
eoh
Posts: 135
Joined: 2001-07-20 09:00
Location: REAL:DREAM
Contact:

Post by eoh »

올려진 2.0.2 는 확인해본 결과 정상입니다.

예제에서는 죽지 않는걸로 보아서, 제가 확인하지 못하는 상황에서 죽는가능성일수도 있습니다.

죽는 상황에 대해서 좀더 자세하게 보고해주시길 바랍니다.
그리고, bind 에서 죽는게 확인되신다면, bind 이후로도 추적해보시는것도 도움이 될것 같군요.

아참. 그리고 저는 VisualC++이 없으므로, 유감스럽게도 그쪽에서 어떻게 돌아가는지 직접 확인할 수 없습니다..
일례로, GCC에서, UTF8의 인식문자때문에 컴파일이 되지 않기에, 강제로 지워버리기도 했습니다. 이에 대해선, VisualC++에서의 문제의 소지는 분명 존재할거라고 생각합니다. 이부분도 확인해보시고요. (만약, IDE 상에서 한글이 제대로 보인다면, 출력도 제대로 되지 않을까 생각합니다만..)
비회원

Post by 비회원 »

저는 Visual C++.net 2003으로 컴파일 했습니다.

그리고, freetype218mt.lib 라는 라이브러리를 구하지 못해서 freetype을 사용하지 않았습니다.

main.cpp에서 아래부분 주석 처리하고

//#include <glan_fimp_freetype>

이렇게 썼습니다.

// glanFont* font1 = new IS::freetype("georgia.ttf");
glanFont* font1 = new IS::englishFont("eng.fnt");
glanFont* font2 = new IS::hangulFont("han.fnt");

죽는 부분은

glanGlyph* glanFontSystem::bind_( ISqchar ch__ )

이 함수가 호출되면

c_pfont_->write_hint( *pGlyph );

이 부분에서 write_hint()라는 함수가 문제를 일으킵니다.

자세한 상황은 다음과 같습니다.

virtual glanGlyph* write_hint( glanGlyph& glyph__ )
{
// IS_ASSERT( glyph__.wide_ == true, "invalid glyph type" );

ISword begin = 0, end = 0;

if ( ch_ <0xAC00>find_occupiedbit_( 16, 16, &data_[pos], begin, end ); // 여기서 죽습니다.
this->extractbit_(16,16,begin,end,&data_[pos], glyph__.data_, blend_replace() );
}

...
}

아마도 data_ 배열의 buffer overflow가 아닌가 합니다.

참고해 주세요
rotarykim
Posts: 15
Joined: 2006-06-26 10:55

부연...

Post by rotarykim »

함수 부분이 잘못 올려졌네요.

제가 함수를 수정한 것이 아니라, 게시판 올릴때 부분적으로 삭제된거 같습니다.

저때 ch_ : 23236, pos : 62848

입니다.

올려진 프로젝트의 문자열 그대로 테스트 한 결과입니다.
eoh
Posts: 135
Joined: 2001-07-20 09:00
Location: REAL:DREAM
Contact:

Post by eoh »

23236 = 0x5AC4
가 입력대상으로 들어갔군요. 유니코드표에서 찾아보니 女+原 의 한자네요.

glan_fimp_bitmap.h:134 에 ch__ <= 로 상한선을 제한하는 부분이 누락되어 있었군요. 이 부분은 고쳐서 올려두겠습니다.

그러나, 근본적인 문제는 왜 저 코드가 들어갔는가 입니다. 위에 언급한 버그를 고쳐도, 공백의 크기를 돌려줄것입니다.
제가 전에도 말씀드렸듯이, main.cpp 초기의 0xFFFE 를 지웠습니다. 따라서 일부 에디터에서는 유니코드로 인식하지 못할수도 있습니다. 그 부분을 해결하지 못하면, 한글 출력을 할 수 없을것입니다. 다음 사항을 확인해 보시기 바랍니다.
  • 1) 일단 에디터에서는 한글문자가 잘 보이나요?
    그렇지 않다면, 새로 문서를 만드시고 노트패드에서 읽으셔서 붙여넣기를 통하여
    새로이 UTF-8 로 저장해보시기 바랍니다.

    2) 에디터에서 UTF-8 형식의 문서로 잘 인식하고 있나요?
    그렇지 않다면 역시 위의 방법으로 UTF-8 문서로 저장해보시기 바랍니다.

    3) 그래도 안된다면, UTF-16 으로 저장하시는 방법을 사용해보세요.

    4) 리터럴 문자열을 정의할때 L을 제거하여 보세요. (각 형식에 대해서)

    5) 그럼에도 문제가 발생하였다면, 코드를 완성형 한글로 저장하신후
    U_("한글텍스트").c_str() 을 L"한글텍스트" 대신 이용해 보시기 바랍니다.
이 부분은, 제가 어찌할 부분이 아니라, 컴파일러에서 어떤코드를 어떻게 해석하느냐의 문제입니다. 따라서, 일단 그 사양에 맞추어 보세요.. U_() 는 그러한 문제를 조금이나마 덜기 위해서 완성형 한글을 유니코드로 바꾸는 함수로서 만들어진것입니다.

그리고, FreeType 는 http://freetype.org 에서 구하실 수 있습니다.
rotarykim
Posts: 15
Joined: 2006-06-26 10:55

그랬었군요~

Post by rotarykim »

Visual Studio 저장 옵션을 [유니코드(서명 있는 UTF-8) - 코드 페이지 65001] 이걸로 바꿔준 뒤

컴파일 하니 문제가 해결됐습니다.

처음엔 [한국어 - 코드 페이지 949] 였는데 디버깅 해보니 과연 글자가 깨져 보였습니다.

서명 있는거랑 없는게 무슨 차이가 있는지는 모르겠지만, 암튼 저 옵션으로 해결이 되었습니다.

덕분에 그동안 머리 아팠던 한가지 문제가 해결됐습니다. ^^

좋은 라이브러리 감사히 쓰겠습니다.~
eoh
Posts: 135
Joined: 2001-07-20 09:00
Location: REAL:DREAM
Contact:

Post by eoh »

서명이라 하시면, 0xFFFE를 의미하는것 같습니다.
뒤늦은 보충일지도 모르겠지만, 유니코드 문서들은, 0xFFFE 를 문서 처음에 넣어두는 경향이 있습니다. 특히 UTF16의 경우 엔디안 방식을 확인하기 위해서 0xFFFE 는 반드시 들어갑니다. UTF8 문서의 경우 0xFFFE가 들어가도 되고 들어가지 않아도 인식하는 경우는 많으나, 0xFFFE 가 들어가는것이 일반적으로 MS에서의 UTF8문서에 대한 기본 방침인것 같습니다. (자세히는 모르겠습니다만)

(전에도 말씀드렸지만) GCC 3.4.4 의 경우에는 오히려 0xFFFE 가 들어가게되면 문서를 인식하지못하게 되더군요. 그래서 저는 문서 제일 처음의 그 코드를 삭제한것입니다.

결국, 문제가 해결되셨다고하니 다행입니다..

그동안 수고하신 보람이 있으시길 빌겠습니다. :)
seeper
Posts: 1483
Joined: 2003-06-06 23:19
Contact:

Post by seeper »

부연...

0xFFFE 는 BOM(Byte Order Mark: 바이트 순서 표시 문자) 이라고 해서 엔디안을 확인할때 씁니다.
컴파일러 기준이 아니라 cpu의 엔디안 방식입니다.
대충 게임치트를 해보셨거나 메모리 바이트단위로 디버깅해보셨다면 이해하시기 편하실것 입니다.
치트로 10000골드를 넣어주려면 0x2710 로 하면 안되고 0x1027로 해줘야죠.

빅엔디안일 때는(SUN CPU 생각하면 됩니다.) 0xFFFE 가 메모리상에 0xFF, 0xFE로 저장이 됩니다.

하지만 리틀엔디안 (우리가 주로 쓰는 인텔 계열 CPU 입니다.)에서는 0xFEFF 라고 저장실제로 바이트로 저장될때는 0xFF, 0xEF 로 저장이 됩니다.

결국 엔디안 방식 때문에 0x5A,0xC4를 0x5AC4 로 해석할수도 있지만 0xC45A 일 수 있습니다.
따라서 BOM은 엔디안 방식에 상관없이 보이게 할수 있는 표시라고 생각하시면 됩니다.

최종적으로 메모리에 저장되는것은 아래의 순서가 되겠습니다.
Big-endian UTF-16 : 0xFE, 0xFF
Little-endian UTF-16 : 0xFF, 0xFE
Big-endian UTF-32 : 0x00, 0x00, 0xFE, 0xFF
Little-endian UTF-32 : 0xFF, 0xFE, 0x00, 0x00

따라서 정식으로 읽으려면 BOM을 반드시 확인해야합니다. (하지만 귀찮으니 인텔이라는 가정을 하죠. ^^)
하지만 저장할때는 플랫폼 신경쓸 필요없이
UTF-16의 BOM은 0xFEFF(unsigned short, WORD) 이고
UTF-32의 BOM은 0x0000FEFF(unsigned long, DWORD) 입니다.

ps...
gcc의 경우 unicode 읽기 처리가 제대로 안된것 같습니다.
아직까지 소스를 unicode로 하기에는 두려움이 앞서는군요.. -_-;;;
seeper0 (a) gmail.com [email주소 무단수집거부]
eoh
Posts: 135
Joined: 2001-07-20 09:00
Location: REAL:DREAM
Contact:

Post by eoh »

부연설명 감사드립니다..

실제로 유니코드 정의(버전4기준)에서는, 0xFEFF에 대해서 바이트순서를 비교하기위해서 0xFFFE 와 비교될 수있다는 가능성만을 정의합니다. 그러니 엔디안 영향이 없는 UTF8 에서 그 문자가 없다고 형식이 제대로 안갖추어 졌다고 볼 수는 없다고 생각됩니다. 메모장에서도 잘 읽히고 (..), 있으면 구분이 명확히 되는 장점이 있지만요.

MingW 에서는 윈도를 이용한 로케일 이외에 표준적인 로케일 지원을 하지 않고 있어서, wchar_t 의 경우 로케일이 제대로 지원되지 않습니다..
그러나 소스파일의 유니코드 형식에 대해선, UTF8 형식에 대해서 처리를 해주되, 바이트 오더 문자의 해석은 하지 않는것으로 보입니다. Eclipse 에서도 이를 고려해서인지 (는 모르겠지만), UTF8 형식으로 저장하는 파일에 대해서 붙이지 않고 있고요.

즉, 이번 문제처럼.. VC++에서는 컴파일러상에서 UTF8에 대해서도 바이트오더문자를 필요로 하고 있기 때문에, VC++과 GCC에서 동시에 존재하는 사용하는 유니코드 소스는 컴파일이 잘 안될수 있다는 문제가 있군요. 형식이 조금 다를뿐, 처리가 제대로 안되는것은 아니라고 봅니다.. 그리고 일반적인 문서를 읽으신다고 하면, 당연히 엔디안은 모두 고려하시는것이 좋겠지요. (메모장처럼)

UTF8 로 메모장을 이용해 저장하시면, 0xFEFF의 UTF-8형식의 코드(0xEFBBBF)가 붙어있더군요. 이것을 제거하면, GCC(MingW) 3.4.4 에서 UTF8문자열을 L접두어를 붙이는것으로, 컴파일러에서 UTF16 으로 변환해주는것을 확인하였습니다. 최소한 그렇지 않더라도 필요하면, UTF8->UTF16 만의 변경코드를 넣어주면 되니까요.. 그래서 저는 최근의 작업은 모두 유니코드에서 하고 있습니다.
위에서도 언급했지만, Eclipse 에서 UTF8형식으로 저장하고 있는데 GCC상에서 문제없이 잘 컴파일 됩니다.
Post Reply