레이블이 컴퓨터야그인 게시물을 표시합니다. 모든 게시물 표시
레이블이 컴퓨터야그인 게시물을 표시합니다. 모든 게시물 표시

2010년 12월 7일 화요일

Syntax Highlighter 3용 srt 자막 브러쉬 공개

Syntax Highlighter에서 .SMI 자막은 별도의 브러쉬가 필요하지 않다.
XML/HTML 브러쉬를 적용하면 충분히 예쁘게 보여줄 수 있다.

<SYNC Start=500><P Class=KRCC>
자막제작: BLUEnLIVE< br>
<font color=yellow>배포처: http://zoc.kr</font>
<SYNC Start=7500><P Class=KRCC>&nbsp;

하지만, (내가 선호하는) .srt는 적절한 브러쉬가 없다.
물론, XML/HTML 브러쉬를 적용하면 약간 비슷하게 보여줄 수는 있지만, 2% 부족하다.

다름아닌, 시간을 하일라이팅하지 않는다는 한계가 있다.

1
00:00:00,500 --> 00:00:07,500
자막제작: BLUEnLIVE
<font color=yellow>배포처: http://zoc.kr</font>

시간을 제대로 표시해줄 수 있는 .srt 전용 브러쉬를 만들었다.

1
00:00:00,500 --> 00:00:07,500
자막제작: BLUEnLIVE
<font color=yellow>배포처: http://zoc.kr</font>

브러쉬는 아래 링크에서 다운받을 수 있으며,


사용할 때는 아래와 같이 브러쉬를 지정하면 된다.

<pre class="brush:srt;">


2010년 12월 5일 일요일

TS/MTS → MKV 한방에 변환하기

많이 보던 인터페이스. VCi를 최대한 재활용했음.


파일 형식 중에 .ts(또는 .mts)라는 형식이 있다. (Transport Stream)
잡음이 많은 환경을 고려해서 설계된 컨테이너 포맷이다.

온라인 방송에서도 널리 쓰이고, DSLR에서도 가끔 쓰이는 포맷인데, 일반 PC에서 쓰기엔 사소한 문제가 있다.
오류를 방지하기 위해 내부적으로 계속 시간 코드를 삽입해놓았는데, 문제는 파일을 잘라붙여 편집한 경우, 이 시간 코드가 따라다닐 때가 있다는 것이다.
예컨데, 30분짜리 .ts 동영상이 있는데, 여기서 10분~20분의 10분 분량을 잘라낸다고 하자.

가운데 빨간색 영역을 잘라내면…


그러면, 새롭게 생성되는 .ts 파일은 20분짜리가 된다. (30-10 = 20)
그런데, 막상 새롭게 생성되는 .ts 파일을 재생해보면 20분짜리가 아니라 30분짜리라는 정보가 나온다.
그리고, 재생을 해보면 10분~20분이라는 영역은 없다고 나온다.

이런 식으로 나온다. 편집 도구 좀 잘 만드시지… 쩝


이런 문제(?)를 간편하게 해결하기 위해 .ts 파일을 재압축 없이 .mkv 파일로 변환해주는 툴을 하나 만들었다.
(이하 본 프로그램은 TS2MKV로 부름)

이 프로그램의 기능은 별 거 없다. mplayer를 이용해서 ts의 비디오/오디오 raw data를 추출하고 다시 합치는 것.
그리고, 이 과정에서 A/V 딜레이 정보를 확인해서 맞춰주는 것 뿐이다.

예컨데, 아래와 같이, 56분 29초짜리라고 정보가 표시되는 .ts 파일을…


TS2MKV로 변환하면 아래와 같이 16분 1초라고 정상적으로 표시된다.
또한, 아직까지는 .ts 재생필터에 비해 .mkv의 재생필터가 더 빠릿빠릿하게 동작한다는 장점도 얻을 수 있다.


이 프로그램은 아래 링크에서 다운받을 수 있다.



덧1. 이 프로그램을 만들면서 이해가 되지 않는 상황을 하나 발견했다.
H.264의 raw 데이터에 mkv/mp4 컨테이너를 씌우면 2배 느리게 재생되는 현상이 종종 발생했다.
즉, 30fps라면 강제로 60fps로 지정해야 정상재생이 되는 것이다.

그런 현상이 발생하지 않을 때도 있어 정확한 원인은 찾지 못했고, FPS에 관련된 옵션을 추가했다.
 - FPS 2배로 늘이기: FPS를 2배로 늘임. 즉, 2배 빠르게 재생됨.
 - FPS를 지능적으로 보정: 생성된 mkv 파일과 별도로 프레임 수를 강제로 조정한 mkv 파일을 하나 더 생성함.

참고로, 일부 문제 있는 파일을 DGAVCIndex로 읽어보면 정상적인 정보가 나온다. 어쩔…

덧2. 이 프로그램은 카라 팬인 친구가 카라 동영상 관련 부탁을 해서 만든 것이다.
그래서, 이 프로그램의 아이콘은 카라다. ㅋㅋㅋ

덧3. 친구야! 술 사라! 공짜가 젤 비싸다. ㅋㅋㅋ


2010년 12월 2일 목요일

멀고도 험했던 코딩용 글꼴 선택기

개발용 글꼴을 선택하는 것은 결코 쉽지가 않다.

일단, 고정폭 글꼴 중에 선택하는 것이 기본이다.
물론, 숫자 0과 알파벳 O가 쉽게 구분되는가, 대문자 I, 소문자 l, 숫자 1이 쉽게 구분되는가 등도 중요 기준이다.
게다가, 한글이 제대로 표시되는가도 확인해야 되고, 한자, 유럽어 등의 표시도 확인해야 된다.

보통 잘 만들어진 개발용 글꼴에 한글 글꼴을 fontlink 기능을 이용해서 연결하는 것이 일반적이었다.
하지만, 이 바닥에 혜성처럼 등장한 강자가 바로 (내가 굉장히 싫어하는) 네이버의 나눔고딕코딩이었다.

나눔고딕코딩을 사용하면 일단 fontlink라는 기능에 대해 몰라도 되는 것 같다. 얼마나 대단한가?

하지만, 나눔고딕코딩은 결코 만능이 아니다.
단지, 한글과 영문자가 나름 예쁘게 만들어져 있을 뿐 한자나 유럽어 등이 하나도 들어있지 않은 것이다.
그래놓고선, 개발용 글꼴이라니… 역시 네이버라는 생각이 들었다.

각설하고, 내가 좌충우돌 끝에 선택한 글꼴은 Consolas + 굴림체 + MS UI Gothic + PMingLiU + SimSun.

깔끔하고 예쁜 글꼴 + 한자/유럽어 칼지원


글꼴 자체가 예쁜 것은 물론이고, 필요로 하는 글꼴들이 모두 윈도우 XP에 내장되어 있다는 것이 가장 큰 장점이었다.
유일하게 추가로 설치해야 되는 Consolas는 MS 홈페이지에서 다운받을 수 있다.


차선책으로 선택한 것은 나눔고딕코딩 + Vera Sans Mono + PMingLiU + SimSun + 굴림체 + MS UI Gothic.


나눔고딕코딩에서 지원하지 못하는 예쁜 유럽언어를 Vera Sans Mono를 통해 표시하는 것이다.
나눔고딕코딩과 Vera Sans Mono를 다운받아야 하는 번거로움이 있고, 글꼴의 크기를 키우면 크기가 미묘하게 맞지 않는 단점은 있지만, 전체적으론 준수해보인다.


덧. 위에서 띄운 창을 나눔고딕코딩으로 설정하면 아래와 같다.
     이게 뭐니, 이게? 응?

유럽어를 다 날려버리는 무식한 글꼴. 결국 아스키+한글 뿐.


2010년 11월 30일 화요일

JW Player 3.1c 재업데이트

그동안 JWPlayer가 5.3으로 업데이트되었다.
플래쉬와 더불어 HTML5를 지원하는 등 많은 변화가 있었다.

설치는 아래 파일을 다운받아 압축을 푼 뒤에 스킨 업로드를 통해 업로드 하고 나서,


스킨의 <body> 영역 맨 위에 아래의 코드를 삽입하면 된다.

<script type="text/javascript" src="http://블로그 이미지 주소/j_FLVPDF.gif"></script>
<script type="text/javascript">
//<![CDATA[
  FLVPDFHelper.all(
    1,
    1,
    680, 500,
    "http://블로그 이미지 주소/s_player.gif");
//]]>
</script>

덧1. 주의할 점이 하나 있다… 기존 버전에 비해 로고 기능이 빠졌다.
그래서, 8행 다음줄에 있었던 로고 파일 지정이 없어졌다. OTL

덧2. 물론, 기존에 있었던 외부 mp4 파일 재생 기능, 구글닥스를 이용한 PDF/PPT 임베딩 기능 등은 그대로다.

덧3. js 파일은 이전 버전에서 변하지 않았다. 변한 것은 플레이어 코어뿐.

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 유니코드로 변환한다.

동영상 변환시 싱크 문제가 생기는 이유들

by BLUEnLIVE | 2010/11/18 07:46

아이폰 동영상 한방에 변환하는 툴: 완벽 버전 by BLUEnLIVE | 2010/11/15 01:21 아이폰(아이패드, 아이팟 터치)의 단점 중 하나는 동영상을...


1. 기본형: delay

비단 VCi(아이폰용 동영상 변환기) 뿐만 아니라, 동영상 변환 프로그램을 사용해보면 싱크 오류는 빈번하게 발생한다.

그런데, 대부분의 경우 동영상 변환시 싱크 오류가 발생하면 의외로 간단하게 해결할 수가 있다.
A/V muxing 도구들은 대부분 오디오 딜레이를 지정하는 기능이 있고, 딜레이 값만 적절히 지정하면 되는 것이다.

싱크 오류 발생시 원본의 딜레이를 가장 쉽게 확인할 수 있는 도구는 MediaInfo다.

MediaInfo에서 지원되는 동영상 포맷은 거의 무한하다. 단, SKM 같은 미친 포맷은 제외


이렇게 확인된 딜레이 값을 지정하기만 하면 된다.



2. 확장형: MP4Box/.mp4 문제

하지만, MP4Box로 .mp4를 만들 때는 얘기가 다르다.
일부 동영상을 완벽히 저장해주지 못하는 치명적인 문제가 있다.
(이것이 .mp4 포맷의 한계인지, MP4Box의 한계인지는 모르겠음)

아래 그림을 보자.


그림 1과 같은 원본 비디오를 MP4Box를 이용해서 mp4로 변환하면 맨 앞의 v0 영역을 날려버리는 경우가 있다.
그러면 신경써서 delay를 넣어주더라도 싱크는 깨지는 것이다.

이러한 경우는 원본 비디오와 생성된 비디오의 길이를 비교해서 이 값을 딜레이에 반영하는 것으로 해결할 수 있다.



3. 극악의 확장형: 원본 비디오가 잘린 경우

사실, 2번의 경우 자체는 거의 완벽한 해결책이 있다.
바로, 원본 비디오와 생성된 비디오의 길이 차이를 비교하는 것이다.

그런데, 여러가지 이유로 파일이 잘린 경우(700MB 중 300MB만 전송된 경우) 원본 비디오의 길이를 알 수 없다.
이 경우는 여러가지 꼼수를 사용해서 원본 비디오의 정보를 추정해야 한다.

하지만, 한 방에 해결되는 완벽한 솔루션은 존재하지 않는다.

게다가, 진짜 문제는 2번인지 3번인지 (또는 1번인지)를 판단하는 것 자체가 쉽지 않다는 것이다.

VCi는 나름 여러가지 방법으로 싱크를 조절하고 있다.
최대한 싱크를 맞추기 위해 노력하고 있지만, 그래도 완벽한 솔루션은 없다.

그렇기 때문에 [VCi]와 별도로 싱크를 보정한 파일인 [VCi-DC]를 생성하는 것이다.

2010년 10월 25일 월요일

오디오/비디오 싱크를 위한 리샘플 헬퍼

수작업으로 비디오를 편집하다보면 오디오의 길이를 조절해야 되는 경우를 만날 때가 있다.
예를 들면 이런 경우...

캠코더로 아이들의 동영상을 찍었는데, 여기에 비틀즈의 <Yesterday>를 입히고 싶다.
그런데, 아이들이 등장하는 장면에서 첫 소절이 시작되게 하고, 사라지는 장면에서 마지막 소절이 시작되게 하고 싶다.
그런데, 노래는 약 2분, 동영상은 1분 50초...

영상에 오디오를 맞추는 것이 일반적이지는 않지만, 이런 경우에는 오디오의 길이를 약간 줄여서 영상에 맞춰야 된다.

그리고, 오디오의 길이를 조절하는 방법은 많다.
굳이 유료 프로그램을 어둠의 경로로 받지 않아도 무료 프로그램도 있고, 간단하게 프로그램을 하나 만들어도 된다.

문제는... 오디오를 얼마만큼 늘이거나 줄여야 되는지를 알기 힘들다는 것이다.

이런 경우가 종종 있어 일일이 손으로 계산하다가, 마침내 귀차니즘을 이기지 못해서 하나 만들었다.

길이를 조절한 뒤에 앞에 73.685초의 오디오 추가


이 프로그램의 기능은 무척 단순하다.
위에서 보는 바와 같이 두 지점의 시간과 그 두 지점이 이동할 시간을 입력하면 적절한 식을 만들어준다.
또한, 원본 오디오의 시간을 입력하면 오디오의 길이를 얼마로 바꿔야 하는지도 함께 알려준다.

남은 일은 적절한 프로그램으로 오디오를 읽은 뒤 길이를 조절하고서 앞을 잘라주거나 무음을 추가하는 것 뿐이다.

이 프로그램은 아래 링크에서 다운받을 수 있다.


2010년 10월 6일 수요일

텍큐닷컴 블로그의 모바일 페이지 만들기

by BLUEnLIVE | 2009/12/12 10:00

티스토리와 텍큐닷컴은 근본도 비슷하고, 기능도 여러모로 비슷하다. 하지만, 세세하게 뜯어보면 지원되는 기능이 미묘하게 다른데, 그 중...



유사한 기반의 티스토리가 간단한 방식으로 모바일 페이지를 지원하는 것과는 달리 텍큐닷컴은 그런 거 없다.
오히려, 구글 코리아는 텍큐닷컴을 버릴 것이란 입장을 명확히 했기 때문에 앞으로도 그런 서비스는 없다.

하지만, 궁하면 통하는 법이요, 모든 솔루션은 구글에서 나오는 법.



1. 모바일 페이지 생성

Google Mobilizer라는 서비스가 있다.
이 서비스를 이용하면 멀쩡한(?) 블로그에서 모바일 페이지를 생성해준다.

이 서비스를 이용해서 본 블로그의 모바일 주소를 추출하면 아래와 같다.

http://www.google.com/gwt/x?u=http://zoc.kr/2

이로써 비교적 간단한 방법으로 모바일 페이지를 만들었다.


2. 자동으로 모바일 페이지로 전환

위의 방법이 간단한 방법이긴 하지만, 그렇다고, 저런 주소를 정말로 모바일 환경에서 입력할 수는 없다.
모바일 페이지에서 접속하면 자동으로 모바일 페이지로 넘어가도록 해보자.

텍큐닷컴의 스킨에서 <body> 바로 아래에 아래와 같은 코드를 삽입하면 된다.

<script type="text/javascript">
//<![CDATA[
if (navigator.userAgent.match(/Mobile|Windows CE|Windows Phone|Opera Mini|POLARIS/) && !navigator.userAgent.match(/iPad/))
    location.href = "http://www.google.com/gwt/n?u=http://(블로그주소)" + location.pathname;
//]]>
</script>

이 코드는 브라우저에서 페이지를 읽었을 때 모바일 환경이라면 위에서 설명한 모바일 페이지로 이동시킨다.


이렇게 만들어진 내 블로그를 아이폰에서 띄우면 아래와 같은 결과가 나온다.
비록 페이지 이동 등의 기능은 정상동작하지 않지만, 아쉬운 대로 쓸 수는 있다. OTL


덧. 본 포스팅 작성시  블로그를 모바일 페이지로 만들기와 자동으로 모바일 페이지로 이동하기가 큰 도움이 되었다.

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년 8월 25일 수요일

캡쳐한 거대 위성사진 쉽게 자르기 (goohwan님 전용) 업데이트(100825)

QAOS에 다음 위성사진 스크린샷 하는 방법좀 알려주세요라는 goohwan 님의 절규어린 질문이 올라왔다.
해결책은 의외로 간단했다.

Firefox + Screengrab! 조합이었다.

그런데, 문제는 이렇게 캡쳐된 이미지의 크기가 무려 6152x6229인데, 주변 여백을 일일이 잘라내야 된다는 거...
작은 이미지라도 잘라내는 것이 번거로운데, 이렇게나 커다란 놈을 잘라내는 건 귀차니즘의 극치다...

이 놈의 원본이 무려 6152x6229, 잘라내고 남은 건 6144x6144다


그래서 프로그램을 하나 간단하게 만들었다. 잘라내야 하는 여백의 크기가 일정해서 만드는 것 자체는 큰 문제가 없었다.
문제가 있었다면... 주말에 회사에 일 하러 들어와서 만들었다는 거...
(혹시 오해는 마시길... 할 일 다 한 다음에 집에 안 가고 만든 거임)

기본값은 물론 다음 Open API + Screengrab!에 맞춰져있음


아래 파일을 다운받은 뒤에 압축을 풀면 된다.
실행파일 하나만 달랑 들었고, 설정 저장 같은 기능은 없다. 1시간 동안 후다닥 만든 프로그램이라... ㅎㅎ


덧2. 1.2로 업데이트됨. 잘라내기 범위를 버튼으로 늘이거나 줄일 수 있으며, jpeg 라이브러리를 빠른 놈으로 교체함.
(2010. 8. 25)

2010년 8월 23일 월요일

트위터 MonkeyFly용 스킨 결정!

egoing 님께서 개발하신 Monkeyfly는 웹기반 트위터의 기능을 극대화시키는 확장이다.
Monkeyfly에서는 css를 활용한 스킨 기능을 지원하는데, 많은 능력자분들께서 css를 공개해주셨다.

그 중 내가 주력으로 사용해온 것은 희주(@jugug) 님의 CSS였다.

한동안 사용하면서 느낀 단점은 지정된 배경색이 원래의 배경을 다 덮어버린다는 거.
(물론, 개발자가 의도한 것임. 내가 느끼기에 단점이라는 것 뿐임)

그래서, 글꼴은 그대로 두고, 배경 및 색깔 부분만 제거해서 쓰기로 했다.
아래는 그렇게 수정된 CSS다.

body #header #logo img
{
    padding-left: 0 !important;
}
#content,.wrapper
{
    border-radius: 5px !important;
}
ul.top-navigation
{
    padding: 0.5em 0;
}
ul.top-navigation a
{
    border-left: 1px solid #cde;
    font: bold 11px tahoma !important;
    margin-right: 1px;
    padding: 0 0 0 5px;
}
ul.top-navigation li:first-child a
{
    border-left: 0;
}
ul.top-navigation a:hover
{
    text-decoration: none;
}
#update_notifications
{
    height: 46px !important;
}
#globalTab
{
    margin-top: 17px;
}
.status-content,.entry-content
{
    font: 13px 나눔고딕 !important;
}
.status-body strong a
{
    font: bold 11px Meiryo !important;
}
td.tdnest:nth-child(even)
{
    background-color: #f5f5f8;
}
#side_base
{
    border-left: 0;
}
#side
{
    width: 198px !important;
}
#side a
{
    font-family: tahoma !important;
    font-size: 11px;
    font-weight: bold;
}
.list-header,.list-header-inner
{
    border-radius: 5px 5px 0 0;
}
#footer-container
{
    min-height: 0 !important;
}
.read-mark
{
    border-bottom: 0 !important;
}
#expand_sidebar
{
    position: fixed;
    top: 82px !important;
}
#pb-side-box
{
    left: 0;
    top: 150px !important;
}
#pb-side-content li
{
    border: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
}
#pb-side-content li a
{
    font: 11px tahoma !important;
    padding: 1px !important;
}
#pb-side-content #primary_nav
{
    float: left;
    padding-right: 5px;
}
#pb-side-content #side_lists
{
    border-left: 1px solid #ddd;
    float: left;
    padding-left: 5px;
}
#pb-side-content .lock-icon
{
    width: 8px !important;
}
#pb-side
{
    border-style: solid;
    border-width: 1px 0 1px 1px !important;
}
#pb-side-top,#pb-side-bottom
{
    left: 0;
}

이렇게 수정된 CSS가 좀 길어보여서 압축해서 사용하기로 했다.
(사실, 이 CSS는 로컬에 저장되기 때문에 전혀 빨라지는 일은 없음 ㅎㅎ)

body #header #logo img{padding-left:0 !important;}#content,.wrapper{border-radius:5px !important;}ul.top-navigation{padding:0.5em 0;}ul.top-navigation a{font:bold 11px tahoma !important;margin-right:1px;padding:0 0 0 5px;border-left:1px solid #cde;}ul.top-navigation li:first-child a{border-left:0;}ul.top-navigation a:hover{text-decoration:none;}#update_notifications{height:46px !important;}#globalTab{margin-top:17px;}.status-content,.entry-content{font:13px 나눔고딕 !important; }.status-body strong a{font:bold 11px Meiryo !important;}td.tdnest:nth-child(even){background-color:#f5f5f8;}#side_base{border-left:0;}#side{width:198px !important;}#side a{font-weight:bold;font-family:tahoma !important;font-size:11px;}.list-header,.list-header-inner{border-radius:5px 5px 0 0;}#footer-container{min-height:0 !important;}.read-mark{border-bottom:0 !important;}#expand_sidebar{top:82px !important;position:fixed;}#pb-side-box{top:150px !important;left:0;}#pb-side-content li{padding:0 !important;border:0 !important;margin:0 !important;}#pb-side-content li a{padding:1px !important;font:11px tahoma !important;}#pb-side-content #primary_nav{padding-right:5px; float:left;}#pb-side-content #side_lists{border-left:1px solid #ddd;padding-left:5px; float:left;}#pb-side-content .lock-icon{width:8px !important;}#pb-side{border-width:1px 0 1px 1px !important;border-style:solid;}#pb-side-top,#pb-side-bottom{left:0;}

수정 및 배포를 허락해주신 희주(@jugug) 님께 다시 한번 고맙다는 말씀을 드립니다.

2010년 8월 10일 화요일

트위터 어플들아! 링크 규칙을 맞춰라! 특히 한글 해쉬태그!

국내 트위터 사용자가 100만명에 달하고 있다. (관련 링크)
이 많은 사용자들이 모두 트위터닷컴을 사용하는 것은 아닐 것이고, 다양한 트위터 어플들을 사용할텐데…
문제는 이 트위터 어플들 간에 가장 기본적이고 중요한 링크에 대한 규칙이 완전히 다르다는 거…

특히, 한글 해쉬태그는 심각하기 짝이 없는 수준이다.
정확히는 트위터 닷컴에서는 한글 해쉬태그를 지원하지 않는다!

다양한 트위터 어플들 중, 내가 사용하는 3종의 트위터 어플 간에 얼마나 링크 해석의 차이가 큰가를 확인해봤다.
출전 선수는 아래와 같다.

- 트위터 닷컴( https://twitter.com ): 레퍼런스다. 이 선수를 빼는 건 말이 안 된다
- 트윗KR ( http://twtkr.com ): 우리나라에선 종종 공식한글판으로도 오인되는 트위터 계의 옴니아2 또는 IE
- Chromed Bird ( Twitter Extension ): 제목 그대로 구글 크롬의 확장


1. 한글 해쉬태그

가장 심각한 문제라고 생각되는 부분이 한글 해쉬태그다.

트위터 닷컴에선 한글 해쉬태그를 지원하지 않는다.
얼마 전까진 #한글_ 의 형식으로 구성된 해쉬태그를 지원했지만, 이제는 더이상 지원하지 않는다.

모든 한글 해쉬태그를 무시하는 트위터 닷컴


트윗KR에서는 트위터 닷컴에서 예전에 지원하던 #한글_ 형식을 지원한다.
오리지널이 어디로 가건 말건 관심 없다.
트윗KR의 무개념은 아래의 크롬드 버드와 비교해보면 더 웃긴다.

엉거주춤한 트윗KR의 한글 해쉬태그


크롬드 버드는 한글을 영문과 동일하게 취급한다.
즉, #한글 (언더바 없음) 형식의 해쉬태그를 그냥 인식해준다.
이 쯤에서 드는 생각… 트윗KR은 왜 굳이 언더바가 포함된 한글 해쉬태그만 인식할까?

차라리 깔끔한 한글 해쉬태그를 지원하는 크롬드 버드



2. 링크 part 1

웹 주소와 메일 주소에서도 해석의 차이가 계속 발생한다.

일단 트위터 닷컴을 보자.
우선 종종 하는 실수인 RT와 http 주소를 붙여서 쓰는 경우, http만 따로 인식하는 상식적인 동작을 보인다.
그리고, 메일 주소는 트위터 아이디와 혼동할 수 있기 때문인지, 별도로 링크처리를 하지 않는다.

명확한 원칙이 느껴지는 트위터 닷컴


트윗KR은 RThttp는 정상적으로 인식하지만, 메일 주소는 개무시하고, 트위터 아이디로 인식한다.
이 문제는 생각해볼수록 웃기기 짝이 없는 문제다.
(트윗KR 개발자들은 이런 문제엔 관심 없나? 아니면, 알아도 무시?)

메일주소를 지멋대로 인식하는 트윗KR


크롬드 버드는 아예 한술 더 뜬다.
RThttp까지 묶어서 링크처리한다. 어쩔~
메일 주소의 일부를 트위터 아이디로 인식하는 정도의 문제는 당연히 지원(?)한다.

주소는 무엇이든 제멋대로 인식하는 크롬드 버드




3. 링크 part 2

웹 주소나 메일 주소 앞뒤에 한글이 붙은 경우의 링크 인식 방식을 확인해봤다.

트위터 닷컴은 모든 경우에서 한글을 무시한다. (알파벳, 숫자, 인가된 기호 몇 개를 제외하고는 모두 무시)
따라서, 아래 캡쳐화면처럼 링크가 아닌 것이 링크로 인식되는 실수는 발생하지 않는다.

그리고, 당연히 모든 메일주소는 무시된다.

역시 명확한 원칙이 느껴지는 트위터 닷컴


트윗KR은 메일 주소의 경우 앞의 2번과 같은 메커니즘으로 동작하기 때문에 한글이 붙건 말건 상관 없다.
웹 링크의 경우, 앞에 붙은 한글은 무시하지만, 뒤에 붙은 한글은 링크로 인식한다. 헐~

메일주소건 한글이 붙은 주소건 제멋대로 인식하는 트 위터KR


크롬드 버드도 트윗KR과 동일하게 동작한다.

트윗KR과 동일한 혼란을 보여주는 크롬드 버드. 어쩔~



물론, 이런 애매한 문제가 발생하는 근본적인 원인은 트위터 닷컴 측의 잘못이다.
링크의 규칙에 대해 정확한 레퍼런스를 정하지 않았을 뿐더러, 종종 바꾸기 때문에 어플 개발자들이 놓칠 수 있다.

하지만, 트위터 어플을 개발하는데, 레퍼런스가 없다면, 가급적 오리지널과 동일하게 동작하도록 하는 것이 상식이다.
그래야 사용자들이 혼란을 느끼지 않는다.
지금의 트위터 어플들은 혼란 그 자체다. 지들이 무슨 Agent of Chaos(조커)냐?

가장 큰 문제라 생각하는 한글 해쉬태그…
뒤에 쓸데 없이 언더바(_)를 붙이는 삽질은 대체 누구를 위한 거냐?
트위터 사용자들의 편리를 위한 거냐? 아니면, 트윗KR 개발자들의 고집을 위한 거냐?

2010년 7월 11일 일요일

자바 스크립트에서 trim()에 대한 단상

자바 스크립트는 trim() 함수를 지원하지 않는다.

그래서 정규식을 활용해서 구현해서 사용하는 것이 일반적이다.

 

주어진 문자열의 앞뒤를 동시에 트림하는 함수를 구현한 가장 일반적인 함수는 아래의 형태이다.

 

function trimboth1(str) {
  return str.replace(/^\s+|\s+$/g,'');
}

그런데, 인터넷을 뒤지다가 Faster Javascript Trim이라는 글을 찾았다.

글의 요지는 간단하다. 정규식을 이용하는 것이 가장 빠른 것이 아니라는 것.

 

코드의 길이가 좀 길어지더라도 더 빠른 함수가 필요하다면 아래와 같은 형태가 좋은 것 같다.

 

function trimboth2(str) {
  var space = '\s';
  var left=str.length, right=-1, i;
  for (i=0; i<str.length; i++) {
    if (space.indexOf(str.charAt(i)) == -1) {
      left = i;
      break;
    }
  }
  for (i=str.length-1; i>=left; i--) {
    if (space.indexOf(str.charAt(i)) == -1) {
      right = i;
      break;
    }
  }
  if (left>right) return '';
else return str.substring(left, right+1);
}

Syntax Highlighter 3.0.83 적용!

by BLUEnLIVE | 2009/12/20 21:02

텍큐닷컴에서는 Syntax Highlighter를 지원한다. 비록 업데이트도 늦고(이건 뭐 파일만 바꿔치면 되는 건데... 쩝...) 한계도 좀 있지만, 고...


이미 이 바닥에선 살아있는 전설이 되어버린 Syntax Highlighter가 3.0으로 업데이트되었다. (이하 SH3이라 부름)


텍큐닷컴은 자체적으로 SH2를 지원하지만, 구버전인 2.0.320만 지원한다.
(지금은 2.1.364, 2.1.382를 거쳐 3.0.83까지 나왔음)

텍큐닷컴판 SH의 가장 큰 장점은 사용법이 무척 단순하다는 점이다.
코드를 입력한 뒤에 코드 앞뒤에 [code 언어] [/code]만 쓰면 끝이다.

하지만, 단순한 만큼 파라미터를 지정할 수 없고, 업데이트는 전무하다는 단점이 있다.
게다가 텍큐닷컴이 폐쇄 또는 합병의 절차를 밟고 있기 때문에 큰 것을 기대할 수도 없다.

몇 가지 상황을 고려해서, 아예 2.x 브렌치를 포기하고 3.x로 갈아타기로 했다.

3.0은 기존 버전에 비해 다음과 같은 개선이 있다.

1. 플래쉬 완전 제거

기존의 1.x, 2.x에서는 copy to clipboard 기능을 구현하기 위해 플래쉬를 사용했다.
하지만, 3.x에서는 플래쉬를 완전히 제거하고 브라우저의 기본적인 복사 기능만으로 이 기능이 동작하도록 구현했다.


2. 화면에서 코드 직접 선택 가능

2.x까지는 코드를 선택해보면 일부분만 선택되었다. (그래서 copy to clipboard 기능이 있었음)
하지만, 3.x에서는 원하는 범위까지 코드의 선택이 가능하다.

게다가 더블클릭하면 코드 전체를 선택해준다.


3. 자동 하이퍼링크 생성

코드 중에 하이퍼링크가 있으면 하이퍼링크를 클릭할 수 있도록 해준다.


4. XRegExp 사용

3.x는 정규식 확장 중 하나인 XRegExp를 사용한다.
(그래서 어쩌라고!)


5. CommonJS 완전 호환

(그래서 어쩌란 거냐고!)


6. CSS 지원 강화

CSS의 지원이 강화되었다. 게다가 SASS 3.0도 지원한다.
(다시 한번 말하는데, 그래서 어쩌란 거냐고!!!)


그런데, 이 SH3를 텍큐닷컴에서 그대로 사용할 수 없다.
적용은 가능하지만, 일부 코드가 정상적으로 출력되지 않는다.

그래서 약간의 수정을 가했다.

1. 치환자 입력을 위해 \#를 #로 변환

기존의 티스토리/텍스트큐브 용 SH에서도 적용했던 건데, 지금 와서 없애긴 뭣해서 다시 적용했다.
\#라고 입력하면 #로 출력해준다.
즉, [#\#_HELLO_#\#]이라고 쓰면 [##_HELLO##_]와 같은 치환자로 변신하는 것이다.


2. 코드 맨 앞과 맨 뒤의 <br /> 또는 엔터 제거

왜 생기는지는 모르겠지만, 가끔 이런 형태가 생기는 경우가 있더라.
그래서, 코드 맨 앞뒤에 엔터나 <br />이 있는 경우 제거하도록 했다.


설치하는 방법은 아래와 같다.

1. 파일 업로드

아래 파일을 다운받아 압축을 푼 뒤에 스킨 편집 → 파일 업로드를 통해 업로드한다.



2. 스킨 수정

아래의 코드를 스킨에 삽입한다.

여기서 주의할 점이 몇 가지 있다.


a. <body>에 코드를 삽입해야 함

<head>가 아니라 <body>의 맨 앞에 넣어야 한다.
텍스트큐브닷컴은 <head> 내에 자바 스크립트 코드를 집어넣으면 씹어먹는다.


b. css는 소문자로 업로드 됨

이것 역시 텍스트큐브닷컴의 특성인데, 이미지 파일은 파일명이 그대로 유지되는 것에 비해, css 파일은 소문자로 바뀌어 올라간다.


c. 블로그 이미지 주소를 찾아서 적어야 함

텍스트큐브의 문제점 중 하나인데, 주소에 "./images/"를 적으면 제대로 인식하지 않는다.
그래서, 블로그 이미지 주소를 찾아서 직접 적어줘야 한다.

※ 류청파(koc/SALM) 님이 피를 토하며 댓글을 달고, 관련 글을 쓰셨다. ./images를 적으면 된다고.
하지만, 이론은 이론일 뿐 실제로 적용해보지 않은 이론은 별 쓸모 없더라.
여전히 텍스트큐브닷컴은 종종 상대주소를 인식하지 못한다.

<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shCore.gif"></script>

<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushMsx.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushAvs.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushNasm8086.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushAS3.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushBash.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushColdFusion.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushCpp.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushCSharp.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushCss.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushDelphi.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushDiff.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushErlang.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushGroovy.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushJava.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushJavaFX.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushJScript.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushPerl.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushPhp.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushPlain.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushPowerShell.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushPython.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushRuby.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushSass.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushScala.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushSql.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushVb.gif"></script>
<script type="text/javascript" src="http://블로그 이미지 주소/images/j_shBrushXml.gif"></script>

<link type="text/css" rel="stylesheet" href="http://블로그 이미지 주소/shthemedefault.css"/>
<link type="text/css" rel="stylesheet" href="http://블로그 이미지 주소/shcoredefault.css"/>

<script type="text/javascript">
//<![CDATA[
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.all();
//]]>
</script>


덧1. SH3는 계속 업데이트 되고, 새로운 테마와 언어 파일을 받을 수 있다.
추가 다운로드는 SH3 홈페이지를 확인하기 바람.


덧2. 내 블로그에서는 주석이 잘못 표시되는 문제가 있었다.
확인해보니, 블로그 스킨(css)에 .comments의 display가 정의되어 있었다.
아래의 내용을 style.css에 추가해서 해결했다.

.syntaxhighlighter .comments {
    display:inline;
}

Notepad2 4.1.24 정식버전 관련 패치 모음

by BLUEnLIVE | 2010/06/26 16:13

notepad2 4.1.24 패치버전 공개 (한글판) by BLUEnLIVE | 2010/03/08 21:10 notepad2 4.1.24 패치버전 공개 (영문판) by BLUEnLIVE | 2010/0...



notepad2 4.1.24 한글 패치에 적용한 패치들을 정리했다.


0. 들어가기 전에

다음과 같은 내용은 모두 준비가 되어있다고 가정하고 설명함.

a. Visual C++ v6.0 및 Service Pack 6 설치
b. Platform SDK 설치 (Windows® Server 2003 R2 Platform SDK ISO Download)
c. Scintilla 2.03 다운로드. 현재 Scintilla는 2.12까지 공개되었으나, 2.03을 사용해야 함


1. Kai Liu님의 패치 적용

np2-4.0.22-allpatches.7z 패치를 적용한다.
이 패치는 총 13개의 패치로 이루어져있으며, 코드 폴딩이나 각종 문법을 포함한 다양한 패치로 구성되어있다.

문제는 이 패치는 Notepad2 4.0.22, 그것도 beta5에 적용 가능한 패치라는 거.
대부분은 그대로 적용가능하고, 일부는 눈치껏(?) 적용할 수 있으나, 2개는 완전히 바뀐 구조에 따라 일일이 적용해야 한다.
특히 문법 패치의 경우, 바뀐 포맷을 유심히 본 후, 문자열 중에 기존 문법에 있는 부분은 그대로 사용해야 되고, 없는 부분은 일일이 notepad2.rc에 문자열을 추가해야 한다.

사용자 삽입 이미지

더 이상의 자세한 설명은 생략한다.


패치 대상 세부 내역 보기..



2. 한글 IME 패치

간단하게 단 하나로 끝난다. (아싸!)

a. notepad2 컴파일 삽질기 8 : 한글 IME 패치


3. 기능 추가

okto 님께서 필요하다고 닥달한 기능들이며, 귀차니 님이 큰 축을 담당하시고, 나도 중간중간 밥숟갈을 들었음.

a. notepad2 컴파일 삽질기 부록#5.2 : context-menu 기능 수정

b. notepad2 컴파일 삽질기 부록#6.1 : 메모장 대신 사용 (수정)

c. notepad2 컴파일 삽질기 부록#4 : AutoHotkey 스킴 추가
   이것 역시 앞의 1번에 따라 열심히 패치한다. 역시 설명은 생략한다. ㅋㅋ

d. notepad2 컴파일 삽질기 부록#11 : 포트란 스킴 추가

e. notepad2 컴파일 삽질기 부록#7 : Shift+마우스 휠에 Page Up/Down 기능 부여

f. notepad2 컴파일 삽질기 부록#9 : 다음 바꾸기(Replace Next) 메뉴에 추가

g. notepad2 컴파일 삽질기 부록#10 : 시간/날짜 삽입 순서 변경

h. notepad2 컴파일 삽질기 부록#12 : NBSP 관련 수정  (2010. 7. 11 추가)

그런데... 패치를 정리한 것 맞나? 뭔가 좀...


notepad2 4.1.24 패치버전 재공개 (한글판)

by BLUEnLIVE | 2010/03/08 21:10

notepad2 4.1.24 패치버전 공개 (영문판) by BLUEnLIVE | 2010/03/07 10:48 지난 3월 2일 드디어 notepad2가 4.1.24로 업데이트되었다....


notepad2 4.1.24의 한글 패치를 공개한지 넉달이 되어간다.
그동안 notepad2 자체는 패치되지 않았지만, Scintilla도 2.12로 업데이트 되고, 기타 수정사항도 있어 손을 좀 댔다.

그런데… Scintilla 2.12를 적용하고 나니 찾기 기능이 잘 동작하지 않았다.
급하게 다시 Scintilla 홈페이지를 뒤져봤더니…

DBCS 환경에서는 무조건 case-sensitive 검색만 지원


DBCS 환경에서는 case-sensitive 검색만 지원하도록 수정되었단다. 헐~

그래서 다시 2.03으로 회귀…
하지만, 문법 분석기(lexer)는 여러모로 업데이트 된 것이 있어 그건 2.12의 것을 사용하기로 했다.


1. Scintilla 2.12중 lexer 적용

얼마 전(정확히는 지난 6월 1일) Scintilla 라이브러리가 2.12로 업데이트 되었다.
속도가 일부 향상되고, 약간의 버그가 수정되었다.

하지만, 전술했듯이 검색 기능이 정상적으로 동작하지 않아 다시 2.03으로 회귀했다.
하지만, lexer는 여러모로 업데이트 되었기 때문에 lexer(Lex*.cxx)들과 SciLexer.h만 2.12의 것을 사용했다.


2. 포트란 스킴 추가

notepad2 컴파일 삽질기 부록#11 : 포트란 스킴 추가에서 설명한 포트란 스킴을 추가했다.


3. 파일명이 notepad.exe인 경우 "메모장 대신 사용" 기능 미동작

(구운고구마 님께서 지적해주신 문제임)
동작 원리상, 파일명을 notepad.exe로 바꾼 경우 메모장 대신 사용을 적용할 수 없다.
파일명이 notepad.exe인 경우 메모장 대신 사용 기능을 선택하면 경고벨소리를 들려주도록 수정했다.
※ 관련 포스트: notepad2 컴파일 삽질기 부록#6.1 : 메모장 대신 사용(수정)


4. NBSP를 ASCII에서 붙이면 공백으로 자동 변환

NBSP(Non-break space)를 ASCII 모드에서 붙일 때 공백으로 변환하도로 수정했다.
※ 관련 포스트: notepad2 컴파일 삽질기 부록#12 : NBSP 관련 수정  (2010. 7. 11 추가)


이렇게 수정된 notepad2는 아래 링크에서 다운받을 수 있으며…


동작화면은 아래와 같다.


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로 복사-붙이기를 실행하면 아래와 같이 동작하는 모습을 볼 수 있다.

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


2010년 6월 26일 토요일

notepad2 컴파일 삽질기 부록#6.1 : 메모장 대신 사용(수정)



okto님 배포본 notepad2는 설치 및 윈도우 메모장 대체를 위해 notepad.inf 파일을 이용했다.
이 방식은 설치가 쉽다는 장점은 있지만, 설치 제거에 손이 많이 간다는 단점이 있다.

장점을 살리고 단점을 제거하기 위해 구차니님께서 메모장을 대체하는 기능을 구현해주셨다.

그런데, 이를 위해 제작/공개한 notepad2 컴파일 삽질기 부록#6 : 메모장 대신 사용에는 사소한 버그가 하나 있었다.
바로 파일명을 notepad.exe로 바꾼 상태에선 동작하지 않는 다는 것이다.

이 부분은 사실 당연한 것이다.
이 기능은 레지스트리의 Image File Execution Options 키를 이용해서 notepad.exe라는 프로그램을 실행하면 다른 프로그램(notepad2.exe)을 실행하도록 지정하는 것인데, 그 프로그램이 또 notepad.exe라서 발생하는 문제이다.

이 기능을 사용하려면 소스 코드를 아래와 같이 수정하면 된다.

반드시 삽질기 부록#5.1 : context-menu 통합 및 수정을 적용한 후에 이 수정을 해야 함

수정 대상 파일은 역시 resource.h, Notepad2.rc, Notepad2.c 세 개이다.


1. resource.h

다음 줄을 추가한다.
#define IDM_REPLACE_NOTEPAD             40692



2. Notepad2.rc

아래 내용을 찾는다.
MENUITEM "Add context-menu to all file type", IDM_REGISTRY_ALL
, CHECKED
이 바로 앞에 다음 줄을 추가한다.
MENUITEM "Replace Notepad",             IDM_REPLACE_NOTEPAD



3. Notepad2.c

앞부분에 다음 변수를 하나 선언한다.
BOOL      bReplaceNotepad;


다음, void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
CheckCmd(hmenu,IDM_REGISTRY_UNKNOWN,bRegistryUnknownType);
또는
CheckCmd(hmenu,IDM_REGISTRY_ALL,bRegistryAllType || bRegistryUnknownType);
이 바로 앞에 다음 줄을 추가한다.
CheckCmd(hmenu,IDM_REPLACE_NOTEPAD,bReplaceNotepad);


다음, LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam) 함수에 다음 case 문을 추가한다.
이 함수 전체가 하나의 switch-case 문으로 되어있는데, case를 하나 추가하는 것이다.
case IDM_REPLACE_NOTEPAD:
if (bReplaceNotepad)
{
//WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe");
bReplaceNotepad = FALSE;
}
else
{
WCHAR path[MAX_PATH], *wTemp;
GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
bReplaceNotepad = TRUE;

wTemp = path;
while (*wTemp) wTemp++;
if (_wcsicmp(wTemp-12, L"\\notepad.exe"))
{
HKEY key1;
LONG res = RegCreateKey(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe",&key1);
if (res == ERROR_SUCCESS)
{
WCHAR cmd[MAX_PATH + 4];
int len = wsprintf(cmd, L"\"%s\" /z", path);
RegSetValueExW(key1, L"Debugger", 0, REG_SZ, (const unsigned char*)cmd, len * 2);
}
else
{
bReplaceNotepad = FALSE;
MessageBeep(MB_ICONEXCLAMATION);
}
RegCloseKey(key1);
}
else
{
bReplaceNotepad = FALSE;
MessageBeep(MB_ICONEXCLAMATION);
}
}
break;


마지막으로 void CheckRegistry() 함수의 마지막 부분에 다음 내용을 추가하면 된다.
res = RegOpenKey(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe",&key);
if(res == ERROR_SUCCESS)
        bReplaceNotepad = TRUE;
else    bReplaceNotepad = FALSE;
RegCloseKey(key);


수정된 결과는 아래와 같다. 짜잔~

사용자 삽입 이미지

수정에 수고해주신 구차니님께 다시 한 번 감사드립니다.

notepad2 컴파일 삽질기 부록#5.2 : context-menu 기능 수정

삽질기 부록#5.1 : context-menu 통합 및 수정을 수정한 포스팅. 부록 #5/#5.1과 본 포스트는 동시 적용불가.

구차니님께서 수고해주신 덕분에 context 메뉴(오른쪽 버튼 클릭 메뉴)를 통해 Notepad2를 사용할 수 있었다.
(삽질기 부록#1 : context-menu 추가 및 삽질기 부록#2 : context-menu 추가 업그레이드 참조)

그런데, Notepad2 3.1.21 정식버전에서는 이 수정을 미묘하게 바꿔야 했으며, 이후 okto님 및 구차니님과 context 메뉴에 대한 수정을 논의한 결과 메뉴를 조금 간결하게 변형하기로 했다.
(File 메뉴에 있는 context-menu 관련 메뉴를 하나만 남기도록 수정)

그리고, 레지스트리에 등록이 실패했을 때의 코드가 빠져있었는데, 실패했을 경우 간단한 경보음을 들려주도록 했다.

삽질기 1~3을 모두 적용했다고 가정하고 시작한다.
그리고, notepad2 컴파일 삽질기 부록#5.1 : context-menu 통합 및 수정은 적용하지 않은 상태에서 시작한다.


수정 대상파일은 resource.h, Notepad2.rc, Notepad2.c의 3개이며, 모두 src 폴더에 저장되어 있다.


1. resource.h

다음 한 줄을 추가.
#define IDM_REGISTRY_ALL            40690


2. Notepad2.rc

IDR_MAINWND MENU DISCARDABLE에서 아래 내용을 찾는다.
MENUITEM "Propert&ies...",              IDM_FILE_PROPERTIES
MENUITEM "Create &Desktop Link",        IDM_FILE_CREATELINK
MENUITEM SEPARATOR
POPUP "&Favorites"
BEGIN
    MENUITEM "&Open Favorites...\tAlt+I",   IDM_FILE_OPENFAV
    MENUITEM "&Add Current File...\tAlt+K", IDM_FILE_ADDTOFAV
    MENUITEM "&Manage...\tF9",              IDM_FILE_MANAGEFAV
END
이 부분을 아래와 같이 수정한다.
MENUITEM "Propert&ies...",              IDM_FILE_PROPERTIES
MENUITEM SEPARATOR
MENUITEM "Create &Desktop Link",        IDM_FILE_CREATELINK
MENUITEM "Add context-menu to all file type", IDM_REGISTRY_ALL, CHECKED
MENUITEM SEPARATOR
POPUP "&Favorites"
BEGIN
    MENUITEM "&Open Favorites...\tAlt+I",   IDM_FILE_OPENFAV
    MENUITEM "&Add Current File...\tAlt+K", IDM_FILE_ADDTOFAV
    MENUITEM "&Manage...\tF9",              IDM_FILE_MANAGEFAV
END

구분자 하나와 context-menu를 추가하는 내용임.


3. Notepad2.h

새로운 함수 하나를 추가.
void CheckRegistry();


4. Notepad2.c

파일 앞부분의 변수 선언부에 다음 내용을 추가.
BOOL      bRegistryUnknownType;
BOOL      bRegistryAllType;


다음, LRESULT CALLBACK MainWndProc(HWND hwnd,UINT umsg,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
case WM_COMMAND:
  return MsgCommand(hwnd,wParam,lParam);
여기에 아래와 같이 한 줄을 추가한다.
case WM_COMMAND:
  CheckRegistry();
  return MsgCommand(hwnd,wParam,lParam);


다음, void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
EnableCmd(hmenu,IDM_EDIT_SELTONEXT,i);
EnableCmd(hmenu,IDM_EDIT_SELTOPREV,i && lstrlenA(efrData.szFind));
EnableCmd(hmenu,IDM_EDIT_REPLACE,i /*&& !bReadOnly*/);

CheckCmd(hmenu,IDM_VIEW_USE2NDDEFAULT,Style_GetUse2ndDefault(hwndEdit));
이 부분을 아래와 같이 수정한다. 1줄을 추가하는 것임.
EnableCmd(hmenu,IDM_EDIT_SELTONEXT,i);
EnableCmd(hmenu,IDM_EDIT_SELTOPREV,i && lstrlenA(efrData.szFind));
EnableCmd(hmenu,IDM_EDIT_REPLACE,i /*&& !bReadOnly*/);

CheckCmd(hmenu,IDM_REGISTRY_ALL,bRegistryAllType || bRegistryUnknownType);

CheckCmd(hmenu,IDM_VIEW_USE2NDDEFAULT,Style_GetUse2ndDefault(hwndEdit));


다음은 LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam)의 수정임.
이 함수는 전체가 하나의 switch-case 문으로 되어있는데, 이 case 중에 IDM_REGISTRY_ALL을 추가함.
case IDM_REGISTRY_ALL:
// Register registry - type all
if (bRegistryAllType || bRegistryUnknownType)
{
if (bRegistryAllType)
{
  //WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_CLASSES_ROOT, L"*\\shell\\Open with notepad2\\command");
RegDeleteKey(HKEY_CLASSES_ROOT, L"*\\shell\\Open with notepad2");
bRegistryAllType = FALSE;
}
if (bRegistryUnknownType)
 {
//WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_CLASSES_ROOT, L"Unknown\\shell\\Open with notepad2\\command");
RegDeleteKey(HKEY_CLASSES_ROOT, L"Unknown\\shell\\Open with notepad2");
bRegistryUnknownType = FALSE;
}
}
else
{
HKEY key1;
LONG res = RegCreateKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2",&key1);
bRegistryAllType = TRUE;
if (res == ERROR_SUCCESS)
{
WCHAR cmd[] = L"Open with &notepad2";
RegSetValue(key1, NULL, REG_SZ, cmd, wcslen(cmd));
}
else bRegistryAllType = FALSE;
RegCloseKey(key1);

  res = RegCreateKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2\\command",&key1);
if (res == ERROR_SUCCESS)
{
WCHAR cmd[MAX_PATH + 4];
WCHAR path[MAX_PATH];
int len;
GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
len = wsprintf(cmd, L"%s %%1", path);
RegSetValue(key1, NULL, REG_SZ, cmd, len);
}
else bRegistryAllType = FALSE;
RegCloseKey(key1);

if (!bRegistryAllType)
{
MessageBeep(MB_ICONEXCLAMATION);
}
}
break;


마지막으로 함수를 하나 추가한다.
void CheckRegistry()
{
    HKEY key;
    LONG res;

    res = RegOpenKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2",&key);
    if(res == ERROR_SUCCESS)
            bRegistryAllType = TRUE;
    else    bRegistryAllType = FALSE;
    RegCloseKey(key);

    res = RegOpenKey(HKEY_CLASSES_ROOT,L"unknown\\shell\\Open with notepad2",&key);
    if(res == ERROR_SUCCESS)
            bRegistryUnknownType = TRUE;
    else    bRegistryUnknownType = FALSE;
    RegCloseKey(key);
}

이렇게 수정하면 아래처럼 File 메뉴에서 context-menu 메뉴 하나가 표시된다.

사용자 삽입 이미지

context-menu가 하나만 나오도록 수정. ^^;


디카 메모리가 부족하다고 풍경을 마음에 담을 순 없다! (캐궁극 버전)

by BLUEnLIVE | 2009/10/14 04:17

이번에도 okto님의 꼬득임에 문득 넘어가버려 기능 추가. OTL (이번에도 너는 이미 뜯어고치고 있다!) 내가 킹크랩을 반드시 뺏어먹고야 말...


디카 사진의 크기를 줄여주는 프로그램에 jpeg SIMD extension 라이브러리를 적용했다.
덕분에 기존 버전에 비해 속도가 엄청나게 빨라졌음은 물론, 파일의 크기가 훨씬 작아졌다.

기존 버전도 120,832바이트라는 엄청나게 작은 크기를 보여줬지만, 이 버전은 단  98,304바이트밖에 되지 않는다!

이 프로그램은 정말 작기 때문에 여행을 갈 때 디카 메모리 한 구석에 넣어두고 쓰면 유사시에 큰 도움이 될 것이다!


물론, 실행 화면은 아래처럼 거의 바뀐 게 없다. ㅎㅎ