레이블이 유니코드인 게시물을 표시합니다. 모든 게시물 표시
레이블이 유니코드인 게시물을 표시합니다. 모든 게시물 표시

2010년 11월 20일 토요일

유니코드를 처리하지 못하는 Subtitle Workshop

by (Sleepy) | 2008/12/08 19:15

자막 작업의 왕, Subtitle Workshop. from IT, PC/• Software 2008/12/08 19:15. 오늘 소개할 Subtitle Workshop은 감히 자막 편집기의 최고봉이라 말할 수 있을 것 같다. 자막 제작, 수정, 변환, 오류정정에 이르기까지 자막에 관련된 모든 작업을 지원 ...


Subtitle Workshop은 자막을 제작하고, 다른 포맷으로 변환하는 데 있어 굉장한 능력을 보여준다.
아마도 자막 관련 통합 툴 중에서 이만한 완성도를 보이는 프로그램은 없었던 것 같다.

하지만, 유니코드를 전혀 처리하지 못하는 치명적인 단점이 있다.


1. 유니코드(BOM) SAMI 미지원

이 점에 대해 우선 분명히 짚어야 할 점은 유니코드 SAMI라는 포맷 자체가 비정상적인 포맷이라는 것이다.

SAMI Structure and Declaration

Because SAMI is based closely on HTML, it has a similar (but not identical) component construct that makes it an easy format to learn and edit. All SAMI documents must begin with a <SAMI> tag and end with a </SAMI> tag.


<Understanding SAMI 1.0에서 발췌>


MS에서 작성한 Understanding SAMI 1.0에서 분명히 얘기하듯이 SAMI라는 포맷은 HTML에 기반을 둔 포맷이다.

하지만, HTML과 달리 텍스트 인코딩을 지정하는 기능이 없다.
SAMI의 한글 코드는 묵시적으로 확장 완성형 한글을 사용하고 있어 유니코드의 글자를 모두 표시할 수 없다.

우리나라의 사용자들은 이런 상황을 극복(?)하기 위해 그냥 SAMI 파일을 통째로 유니코드로 저장하기로 했다.
또한, 플레이어를 만드는 개발자들도 이에 동조해서 유니코드 포맷의 SAMI를 처리하고 있다.

그런데, SAMI의 기본을 생각해보면 SAMI에서 유니코드를 사용하려면 부호 있는 UTF-8을 사용했어야 했다.
하지만, 이젠 늦었다. 그냥 이렇게 갈 수 밖에 없다.

각설하고, 이 말도 안 되는 유니코드 SAMI는 Subtitle Workshop에서 읽을 수 없다.
그리고, 워낙에 말도 안 되는 포맷이라 앞으로도 Subtitle Workshop에서는 지원하지 않을 것이다.


2. SRT/UTF-8 미지원

이건 유니코드 SAMI와 완전히 다른 얘기다.

SRT 파일은 ANSI 포맷 외에, UTF-8 포맷으로도 저장할 수 있다.
특히, 아이폰 내장 비디오 플레이어는 UTF-8 포맷의 SRT 파일만 재생이 가능하다.

아이폰 재생 화면: 자막은 [아바타] 감성 버전. 영상은 전혀 무관한 [딱따구리]


즉, SRT는 SAMI와 다르게, 기본적으로 유니코드를 지원한다.

하지만, Subtitle Workshop 이 SRT/UTF-8를 전혀 읽거나 쓰지 못한다.


3. 글자 속성의 파괴

이 부분은 정말 이해가 되지 않는 부분이다.

SAMI는 (다른 자막 포맷과 마찬가지로) 다양한 글자 속성을 지원한다.
<b>, <i>, <font>, <u> 등등...
지원되는 속성와 형식은 SRT도 유사하다.

그런데, Subtitle Workshop는 어떤 글자 속성이든지 <i>로 대체해버린다.
난 한동안 SRT는 <i>밖에 지원하지 않는다고 생각할 정도였다.


결론적으로, Subtitle Workshop은 유니코드 SAMI를 SRT/UTF-8로 제대로 변환하지도 못하고, 글자 속성도 파괴한다.
자막 작업의 왕이라고 부르기엔 너무 커다란 헛점이 있는 것이다.

VCi를 만들면서 자막 변환 부분을 직접 제작한 이유가 바로 이런 점 때문이었다.
VCi는 유니코드 포맷 자막을 직접 읽어 완전하게 UTF-8 유니코드로 변환한다.

2010년 9월 26일 일요일

한글 풀어쓰기→모아쓰기를 통해 얻은 지식들

by BLUEnLIVE | 2010/09/24 09:30

아이폰은 단점이 없는 완벽하고도 지고지순한 폰이 아니다. 철학이 뚜렷하고, 이에 따른 장단점이 명확히 나뉘는 폰이다. 그런데, 그런 원인...



1. 유니코드의 한글자소는 초중종성 정보가 있는 것과 없는 것이 별도로 존재

유니코드에는 (당연히) 음절 단위의 글자 외에도 한글자소가 별도로 존재한다.
그런데, 이 자소에는 초중종성의 정보가 있는 자소와 없는 자소가 별도로 존재한다.

일단, 정보가 있는 자소U+1100 부터 시작된다.
현대어→고어 순으로 할당되어 있으며, 현대어에는 ㅄ같은 문자가 초성에 할당되어있지 않았다.

초중종성 정보가 포함된 한글자소: U+1100부터 시작됨. 아래 표시된 부분은 종성 'ㄱ'


초중종성 정보가 없는 자소U+3131 부터 시작된다.
여기는 자음에 초성과 종성이 혼합되어 있으며, 이 순서는 완성형 한글의 순서와 정확히 일치한다.



2. 두 종류의 자소(U+1100, U+3131)를 완성형으로 변환시 문제 발생 가능

VC6은 기본적으로 유니코드가 아닌 완성형 코드를 지원한다.
그리고, 시스템에서 이를 유니코드로 변환할 때는 U+3131로 변환한다.
즉, 아래와 같은 코드를 실행시켰을 때...

CFileFind finder;
BOOL bOk = finder.FindFile(_T("c:\\*.txt"));
while (bOk)
{
  bOk = finder.FindNextFile();
  if (finder.IsDots() || finder.IsDirectory()) continue;
  _trename(finder.GetFilePath(), _T("c:\\말도안돼.txt"));
}

c:\ㅁㅏㄹㄷㅗㅇㅏㄴㄷㅙ.txt가 존재하더라도 한글자소가 U+1100으로 저장되어 있다면 rename되지 않는다.
왜냐하면 _trename()의 내부에서는 이런 순서로 진행되기 때문이다.

API: U+1100 → finder.GetFilePath(): 완성형 변환_trename(API): U+3131 변환

따라서, 멀쩡히 있는 파일명을 API를 통해 읽은 뒤 다른 이름으로 바꾸는 작업이 불가능할 때도 있는 것이다.
물론, 해결책은 있고, 간단하다. 컴파일러를 유니코드 모드로 설정하면 된다.


3. 유니코드 한글은 조합형으로 구성되었음

한글을 조합하는 방법에 대해 이리저리 고민했는데, 알고보니 유니코드의 한글 코드는 조합형으로 구성되어있다.
보통 조합형 한글이라는 개념은 2바이트(16비트)를 1비트(무조건 1)+5비트(초성)+5비트(중성)+5비트(종성)으로 구성하는 방식을 의미한다.


하지만, 유니코드의 한글은 이런 비트를 조합하는 방식은 아니다. (그런 테이블을 할당받을 수도 없음)
U+AC00부터 초성 19자, 중성 21자, 종성 28자를 순서대로 배치하는 것이다.


예컨데, 이라는 글자의 코드는 U+AC00 + 7(ㅂ)*21*28 + 15(ㅞ)*28 + 9(ㄺ) = U+DBC1이다.
이러한 원리를 이용해서 간단한 식만으로 한글을 조합하거나, 자소를 분리해낼 수 있는 것이다.

유니코드의 각국어 할당표를 만드는 과정에서 여러나라들의 수많은 알력이 있었다.
유니코드의 한글구성표를 만드는데 노력하시고, 박터지게 싸우신 모든 분들께 감사드린다.


덧. VC++ 6.0도 유니코드를 기본으로 사용할 수 있음

메뉴에서 Project → Settings... 선택한 뒤, C/C++ 탭에서 Preprocessor definitions:를 찾는다.
여기서 _MBCS를 찾아 _UNICODE로 바꾼다.


다음으로, Link 탭에서 Output을 선택한 뒤 Entry-point symbol:wWinMainCRTStartup으로 지정한다.


2010년 7월 11일 일요일

notepad2 컴파일 삽질기 부록#12 : NBSP 관련 수정

SMI 포맷의 자막을 만들거나, HTML을 직접 작성해본 경험이 있는 사람은 &nbsp;라는 표현을 자주 봤을 것이다.
그런데, NBSP가 뭔지 정확하게 아는 사람은 그리 많지 않은 것 같다.

NBSP는 Non-break Space의 약자로 공백의 일종이지만, 사실 일반적인 공백(0x20)과는 다르다.
줄바꿈을 하지 않는 공백을 의미하며, 유니코드에서는 0xa0이라는 값에 할당되어 있고, ASCII에는 이 개념이 없다.

유니코드를 ASCII 코드로 변환할 때 ASCII 코드에 없는 문자를 변환하는 방식은 정의되어있지 않다.
따라서 표준에 따르면 NBSP를 ASCII로 변환하면 공백으로 만들던, 물음표로 만들던 문제가 되지 않는다.

하지만, 실세계에서는 NBSP는 알아서 공백(0x20)으로 변환해주는 것이 예의다.

notepad2는 WideCharToMultiByte() 함수를 이용해서 유니코드를 ASCII 등으로 변환한다.
그런데, 이 함수에선 NBSP의 변환을 별도로 처리하지 않는다.

따라서, NBSP를 ASCII 모드에서 붙여넣으면 아래와 같은 결과를 보여준다.

구글 크롬에서 Syntax Highlighter 3.0으로 처리한 소스를 복사-붙이기 한 화면


웹 브라우저 중에서 IE나 FireFox에서 복사(copy)를 하면 NBSP를 공백으로 변환한 뒤 클립보드로 보내지만, 구글 크롬은 NBSP를 그대로 내보낸다.
Syntax Highlighter에서 코드를 복사한 뒤에 메모장2에 붙여넣으면 이 현상을 경험할 수 있다.

전술했듯이, 이 현상은 결코 버그가 아니며, 단지 현상일 뿐이다.
하지만, 사용자 입장에서 불편하긴 마찬가지다.

그래서 메모장2를 패치하기로 했다.


1. ScintillaWin.cxx 수정


이 현상을 수정하려면 일단 붙여넣기 부분을 수정해야 된다.
ScintillaWin.cxx 파일에서 void ScintillaWin::Paste() 함수를 찾은 뒤 아래 부분의 위치를 확인한다.

  UINT cpDest = CodePageFromCharSet(
      vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);

  len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
      NULL, 0, NULL, NULL) - 1; // subtract 0 terminator

이 부분을 아래와 같이 수정한다. 몇 줄의 코드를 삽입하는 것이며, 기존 코드의 수정은 없다.

  UINT cpDest = CodePageFromCharSet(
      vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);

  // 여기부터 삽입
  int wlen = wcslen(uptr);
  for (int i=0; i<wlen; i++)
  {
      if (uptr[i] == 0xa0) uptr[i] = 0x20;
  }
  // 여기까지 삽입

  len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
      NULL, 0, NULL, NULL) - 1; // subtract 0 terminator

이것으로 붙여넣기에서 문제는 해결되었다.


2. Edit.c

사실, 이 현상을 경험할 수 있는 것은 붙이기 명령 외에 하나가 더 있다.

유니코드에서 ASCII로 코드 페이지를 바꾸면 이 현상이 발생할 수 있다.
그래서, 코드 페이지를 바꿀 때도 이 문제가 발생하지 않도록 수정한다.

BOOL EditConvertText(...) 함수를 찾아 아래 내용의 위치를 확인한다.

  cbwText  = MultiByteToWideChar(cpSource,0,pchText,length,pwchText,length*3+2);
  cbText   = WideCharToMultiByte(cpDest,0,pwchText,cbwText,pchText,length*5+2,NULL,NULL);


이 내용을 아래처럼 수정한다. 역시 몇 줄의 코드를 삽입하는 것이며, 기존 코드의 수정은 없다.

  cbwText  = MultiByteToWideChar(cpSource,0,pchText,length,pwchText,length*3+2);
  // 여기부터 삽입
  {
    int i;
    for (i=0; i<length; i++)
        if (pwchText[i] == 0xa0) pwchText[i] = 0x20;
  }
  // 여기까지 삽입
  cbText   = WideCharToMultiByte(cpDest,0,pwchText,cbwText,pchText,length*5+2,NULL,NULL);

이렇게 수정한 뒤 구글 크롬 메모장2로 복사-붙이기를 실행하면 아래와 같이 동작하는 모습을 볼 수 있다.

구글 크롬 따위는 겁나지 않는다능!


2009년 6월 21일 일요일

뒤늦게 풀어본 공대생의 시계

사용자 삽입 이미지


워낙 유명한 시계인데다, 답이 공개될만큼 공개된 것이지만, 그래도 한번 풀어봤다.

  1. 르장드르의 상수. x보다 작은 소수의 갯수를 구하는 식을 구하며 만든 상수
    르장드르는 1.08366이라 생각했지만, 이후 1이라는 연구결과가 나옴
  2. 1 + 1/2 + 1/4 + 1/8 + … = 2로 수렴
  3. 유니코드의 16진수 표기법. &#x (또는 #x)는 16진수를 의미하며, 마지막 부호는 세미콜론(;)임
    &#x33; == 0x33 == '3'
  4. 2^x mod 7 은 1, 2, 4가 반복됨. x = 0 → 1, x = 1 → 2, x = 3 → 1, …
    이 규칙을 적용하면 2^(-1) mod 7 = 2^(3-1) mod 7 = 4
  5. 황금비(φ, phi) = (5^0.5 + 1)/2 임.
    이걸 다시 역셈하면 (2*φ-1)^2 = 5
  6. 3! = 3 × 2 × 1 = 6
  7. 6.99999999999… = 7로 수렴
  8. 주판알로 생각하면, ● = 5, ○ = 1 → 5+3 = 8
    또는 2진수로 생각하면 1000(2) = 8(10)
  9. 4진수임. 21(4) = 2 × 4^1 + 1 × 1 = 9(10)
  10. 5C2 = (5×4)/(2×1) = 10
  11. 그냥 16진수. 0x0B == 11
  12. 12^3 = 1728, 1728^(1/3) = 12

하지만, 심플함이 미덕인 오캄의 면도날을 금과옥조로 삼는, 진짜 공대생은 디지털 시계를 사용한다는 거…