삽질하는플머

다국어 입력... 또 하나의 산.

탐구생활/Uniscribe
델마당 겜제작란에 올린 글을 옮겨붙여둠. 

솔직히 지금까지 출력만 잘 해결하면 입력은 윈도가 알아서 해주는 줄 알았는데...
글립엔진을 붙이고 있는 막내가 낑낑거리기에 참견하러 갔더니, 태국어 입력이 엉뚱하게 처리되고 있더라구요. 

앞서 이 길을 걸어가셨던 엘비스님께 도움을 청하니... "응? 그냥 되던데요?" 
태국어에 대해 Zupet님이 남기신 주옥같은 글에는... "그냥 WM_CHAR를 받아 찍으면 된다~"

두 분 모두 키워드는 "그냥~" 음냐... 도대체 안되는 이유가 뭘까 고민하니... 
아... 저희 게임윈도는 유니코드기반이었습니다. 2년쯤 전 중국어 간체로 된 게임제목이 한글윈도에서 깨져보이는 게 짜증나 바꿔버렸습죠. 로케일을 특정 국가로 고정하고 안시 API를 사용할 때에는 입력된 CharCode값을 해당 국가코드로 간주해 조립하면 되지만, 동시에 여러국가 언어를 입력받기 위해 유니코드 지원 윈도를 만들었을 때는 얘기가 다르더라구요. wParam의 LoWord에  255가, HiWord에 해당 UCS2 코드가 박혀날아옵니다. 이걸 Char로 형변환해 찍었으니 요상한 글자가 나올 수 밖에요. 어흙... 

일단 여기까지 해서 한시름 놓았는데... 다시 또 하나의 산이 가로막습니다. 태국어 입력기를 설치하고 'i' 'e' 를 차례로 입력하면 아래와 같은 글자가 찍힙니다. 

รำ

두 개 처럼 보이지만 뒤에 붙은 놈은 앞의 글자를 꾸며주는 물건이고 때문에 이 글자는 하나의 글자입니다. 그런데 여기에 다시 'e'를 눌러보면 어떻게 될까요. 표준 윈도 컨트롤을 제외한 입력창, 심지어 지금 제가 글을 적고있는 구글 크롬에서도 빈 문자가 뒤에 적층됩니다. 때문에 백스페이스로 글자를 지우려 하면 'e'를 누른 횟수만큼 커서가 제자리에 멈추게 되지요. 


รำรำรำ
รำำำรำำำรำ


위의 두 문자는 겉보기에는 똑같이 생겼지만 메모장같은 곳으로 복사해 백스페이스로 지워보면 처음것은 여섯번, 두번째 것은 열 번을 눌러야 지울 수 있습니다. 즉 이런 입력은 허용되어서는 안된다는 얘기입니다. (아이폰용 사파리에서는 입력한 대로 보이는군요.)  

막내와 머리를 쥐어짜기 시작했습니다. 처음에 떠오른 생각은 태국어의 조합원리를 조사해 예외처리를 하자는 것... 하지만 이 방법은 아무리 삽질을 좋아하는 저도 썩 내키지 않더군요. 그래서 새로운 글자가 입력되기 전과 후의 글립모양을 살펴서 동일할 경우 입력을 취소하자는 꽁수를 떠올렸습니다. 이 때 글립폭이 0인 녀석들은 제외하면 실제 보이는 모양만 비교할 수 있겠다 싶었지요.

그런데... 'i' 와 'h'를 입력할 경우처럼 두번째 글립이 앞부분에 옵셋되어 합쳐질 때, 

ร + ่ = ร่

이런 경우 유니스크라이브는 두번째 글립의 폭이 0이라고 알려줍니다. 하지만 모양이 틀림없이 변하므로 빼고 생각할 수는 없습니다. 결국 실제로 폰트에서 뽑아낸 글립의 크기값이 필요해지네요. 유니스크라이브의 고수준 API인 ScriptString을 쓰지 못하는게 처음에는 많이 아쉬웠는데... 그 때 휘둘렀던 삽자국이 이리도 유용하게 쓰일줄은 몰랐습니다. 흐흐흐~~

사실 크롬도 동일한 현상을 보인다는 걸 미리 알았다면, 그냥 사용자가 조심해서 쓰라고 하고 끝냈을텐데... 그건 이 글을 적으면서 깨달은거니... ^^;;

암튼 짧지만 굵은 삽질기였습니다. 솔직히... 이런거 갖고 고민할때가 제일 재미나는 것 같아요~~ 


기존 게임용 에디터 구현... 3.

탐구생활/Uniscribe
한 일

미루어두었던 패스워드 캐릭터 마무리.





테스트삼아 구현해 본 장평. 하나의 폰트로 여러가지 느낌을 줄 수 있어서 의외로 쓸만할 듯.
(자간의 경우는 출력오토마타 문자열에서 문제가 있어 일단 보류. 대학때 레포트의 비결이던 신명조 장평 95% 자간 -5 가 떠오르네.)
 





에디터에서는 써먹을 일이 거의 없겠지만, 글립 랜더링 옵션도 지정할 수 있게 함. 






이제 기획자들이 써먹을 수 있도록 루아스크립트 연동코드만 매달아주면 되겠다.

==============================
2010.8.3

루아 연동코드 추가 완료. 이제 당분간 마음 편하게 미루어두었던 삽질들을 할 수 있겠군.

기존 게임용 에디터 구현... 2.

탐구생활/Uniscribe
지난번에 이어서

한 일

힌두어, 태국어, 아랍어등 출력오토마타 문장에서의 백스페이스 처리.





우측정렬 구현.


IME 처리. 조합시 언더라인, 조합커서, 후보창위치 등.



선택박스 구현.
  (아랍어 등의 RTL 혼용 테스트. 머리 다 빠짐...)


  (컨트롤 + 화살표로 태국어 단어단위 선택 테스트.)






할 일

마우스 메시지 처리.
패스워드문자 구현.

우우... 선택박스에서 힘을뺐더니 귀차니즘이 밀려온다...

--------

마우스 메시지 처리 완료.

패스워드 처리를 위해 생각을 정리하다보니... 랜더링, 커서처리, 마우스처리등을 이중으로 관리하느라 구조가 지저분해진다. 내부문자열을 단순히 "*" 로 대체해서 찍기 위해 이런 수고를 하는 것 보다... 그냥 패스워드 처리 전용 에디터를 하나 더 만드는 편이 맞을 듯 싶다. (생각해보니 예전에도 같은 고민에 같은 결론을 내렸던 것 같다.)

최대길이값을 제한하는 코드만 추가하고 마무리짓자.


--------

MaxLength 처리 완료.
최대값 근방에서 IME 입력 다루기가 거지같네... 특히 일본어처럼 조합문자열이 복수인경우... 예전에도 이게 귀찮아서 안했었는데...

이제 깔끔한 마음으로 술먹으러 가야지...

기존 게임용 에디터 구현... 1.

탐구생활/Uniscribe

글립캐시 DLL을 사용한 에디트 컨트롤을 구현중. 수개월동안 머릿속에서만 돌아가던 물건이 조금씩 완성되는 기분은 참 좋다. 하긴 뭐... 이 맛에 코딩 하는거지만...



한 일.
출력 처리.
키다운 메시지 처리.
글자단위, 단어단위 캐럿 이동.
캐럿 이동에 따른 문자열 옵셋 처리.
복사/잘라내기/붙이기


할 일.
IME 메시지 처리.
마우스 메시지 처리.
선택박스 구현.
패스워드문자 구현.


사실 제일 난감한부분이 바로 "선택박스"인데... 예전에는 그냥 선택시작위치에서 선택된 양 만큼 역상시키면 그만이었지만 LTR, RTL이 뒤섞인 상황에서는 그게 말처럼 쉽지가 않다. 아래 이미지는 위의 문자열을 워드패드에서 선택해 본 것이다.



RTL인 아랍어는 선택상자가 좌측으로 뻗어나가다가 LTR인 영문을 만나 다시 우측으로 진행된 후 다시 아랍어를 만나 좌측으로 진행되고 있다. 유니스크라이브의 ScriptString API 에서는 ScriptStringOut을 쓰면 이 부분까지 자동으로 처리해주지만... 그저 군침만 흘릴 뿐... 

밤새 고민하면 내일쯤 대충 방법이 떠오르겠지 머~~

 

기존 게임으로 피드백

탐구생활/Uniscribe

글립 캐시를 외부에서 사용할 수 있도록 DLL로 만들고, 기존 게임의 랜더링엔진에 대한 아답터루틴을 구현한 뒤 간단한 문자열을 올려보았다. 입력기를 새로 만들는 것 보다 기존 게임에서 쓰이는 입력기의 글자출력부분만 바꿔치는 방법이 좀 더 빠른 작업이 될 것 같아서... 또 어차피 기존 게임도 아랍어 등의 다국어 처리는 필요하니까.




DLL의 크기는 일단 160kb. 폰트링크에 쓰인 MLang 이 COM 이므로, 이에 대응하는 코드를 LVCL에 짜넣으면 훨씬 작게도 만들 수 있을 것이다.

힌두어 + 아랍어 + 태국어도 테스트. 자알~ 출력된다. (뿌듯~ 뿌듯~)
예제문자열 : ठऑक्षझॉيُساوِيเข้าหน้าสู่หน้าหลัก




지나가는 팁 한조각.

구버전 델파이는 코드편집기에 안시문자열을 사용한다. 때문에 위와같은 문자열을 코드에디터에서 지정하기가 쉽지 않다.
UTF-8 편집을 지원하는 상위버전 델파이라면 #숫자 와 같은 형식을 WideString 에 바로 때려넣는것도 가능하지만 

  myWSTR: WideString =
    #2336#2321#2325#2381#2359#2333#2377#1610#1615#1587#1575#1608 +
    #1616#1610#3648#3586#3657#3634#3627#3609#3657#3634#3626#3641 +
    #3656#3627#3609#3657#3634#3627#3621#3633#3585;

구버전에서는 이또한 여의치 않다.

유니코드 구세주인 TNT컨트롤의 TntSystem.pas 에 정의된 다음 두 함수를 써보자. 

  function WideStringToUTF7(const W: WideString): AnsiString;
  function UTF7ToWideString(const S: AnsiString): WideString;

WideStringToUTF7 함수로 위의 다국어를 변환해주면 다음과 같은 안시문자열을 얻을 수 있다.

      '+CSAJEQkVCU0JNwkdCUkGSgZPBjMGJwZIBlAGSg5ADgIOSQ4yDisOGQ5JDjIOKg4' +
      '5DkgOKw4ZDkkOMg4rDiUOMQ4B';

코드를 안짜고 간단히 얻으려면, 빈 폼에 TTntEdit 를 하나 올리고 여기에 원하는 다국어를 넣어준 뒤 마우스 우측버튼을 눌러 "View as Text" 메뉴를 선택하면 Text_UTF7 프로퍼티에 생성된 문자열을 복사해올 수 있다.




이렇게 얻어진 문자열을 대충 다음과 같이 UCS2로 변환해쓰면 된다. 

const
  myASTR :  AnsiString =
      '+CSAJEQkVCU0JNwkdCUkGSgZPBjMGJwZIBlAGSg5ADgIOSQ4yDisOGQ5JDjIOKg4' +
      '5DkgOKw4ZDkkOMg4rDiUOMQ4B';
var
  myWSTR: WideString;
......
  myWSTR := UTF7ToWideString(myASTR);

뭐 구버전 델파이의 코드에디터에서 유니코드를 직접 지정할 일이 얼마나 될까 싶지만...
(적어도 오늘까지는 단 한번도 없었... ^^)