2013年1月22日 星期二

DVD 字幕擷取

在 Linux 下從 DVD 裡擷取字幕真不是件容易的事。擷取的方法不只一種,但是抓出來的東西真的能用的卻不多。像是 mencoder、Avidemux 它們雖然可以抽出 idx/sub 字幕檔,但是仔細校對以後發現它的時間軸會偏掉(個人猜測是 VFR 的緣故)。

個人在嘗試許多方法以後找到一個比較能接受的解:用 transcode 解 sub 字幕檔。這個工具解出來的 sub 字幕雖然時間軸稍微偏個幾毫秒,不過只要平移一下就能解決。

詳細方法如下:

工具:
  1. mplayer
  2. transcode
  3. subtitleripper
  4. Aegisub
步驟:
  1. 先用之前文章提到的方法解出目標 .vob 檔案
  2. 用 transcode 提供的工具解出 .vob 檔中的 .sub 字幕
    $ tccat -i target.vob | tcextract -x ps1 -t vob -a 0x20 > target.sub
    其中 0x20 是語言碼。0x20 是中文,0x22 是英文。
  3. 用 subtitleripper 提供的 subtitle2pgm 產生 .pgm 圖檔和 .srtx 字幕時間軸
    $ subtitle2pgm -o output_prefix -c 0,255,0,0 < target.sub
這樣會產生許多字幕圖檔(黑底白字)和我們需要的 .srtx 範本

  • 以 Aegisub 載入 .srtx 字幕調整時間軸並修改翻譯
  • 2013年1月8日 星期二

    Pango Cairo 畫游標

    Pango Cairo 可以靠 pango_layout_get_cursor_pos 這個 function 來取得游標的位置(像素為單位),只要傳字串的 index(單位為 byte)就可以知道該字元的游標應該畫在哪裡。
    PangoRectangle strong;
    PangoRectangle weak;
    
    pango_layout_get_cursor_pos(pango_layout, strlen("あい"), &strong, &weak);
    
    cairo_line_to(cairo_context, strong.x/PANGO_SCALE, strong.y/PANGO_SCALE);
    cairo_rel_line_to(cairo_context, 0, strong.height/PANGO_SCALE);
    cairo_stroke(cairo_context);

    2013年1月7日 星期一

    XIM client 範例程式

    這個程式示範如何用最少的程式碼在 X window 程式中接收 XIM 輸入法的輸入字串。Input style 為 Over the spot。
    #include <stdio.h>
    #include <string.h>
    #include <X11/Xlib.h>
    #include <X11/Xutil.h>
    #include <X11/Xatom.h>
    #include <X11/Xlocale.h>
    
    #define TITLE_STRING "XIM test"
    
    int main(int argc, char **argv)
    {
    	int quit = False;
    	Display* display = NULL;
    	Window window;
    	XTextProperty  title;
    	XEvent event;
    	Atom wm_delete_window;
    
    	XIM xim;
    	XIC xic;
    	int xim_status;
    	KeySym keysym;
    	int count = 0;
    	unsigned long fevent;
    	char buffer[256]= {};
    
    	/* Call XSetLocaleModifiers before initialize XIM */
    
    	setlocale(LC_ALL, "");
    	XSetLocaleModifiers("");
    
    	display = XOpenDisplay(NULL);
    	window = XCreateWindow(display, RootWindow(display, 0), 0, 0, 512, 512, 0,  CopyFromParent, CopyFromParent,  CopyFromParent, 0, NULL);
    
    	XSetWindowBackground(display, window, BlackPixel(display, 0));
    
    	title.value = (unsigned char*)TITLE_STRING;
    	title.encoding = XA_STRING;
    	title.format = 8;
    	title.nitems = strlen(TITLE_STRING);
    
    	XSetWMProperties(display, window, &title, &title, NULL, 0, NULL, NULL, NULL);
    
    	wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
    	XSetWMProtocols(display, window, &wm_delete_window, 1);
    
    	XMapWindow(display, window);
    
    
    	/* XIM initialization */
    
    	xim = XOpenIM(display, NULL, NULL, NULL);
    	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing ,XNClientWindow, window, NULL);
    
    
    	/* We need IC event to switch IME */
    
    	XGetICValues(xic, XNFilterEvents, &fevent, NULL);
    
    	XSelectInput(display, window, ExposureMask | KeyPressMask | FocusChangeMask | StructureNotifyMask | fevent);
    
    	XSetICFocus(xic);
    
    	while(!quit)
    	{
    		XNextEvent(display, &event);
    		if(XFilterEvent(&event, None))
    		        continue;
    
    		switch(event.type)
    		{
    		case KeyPress:
    			count = XmbLookupString(xic, &event.xkey, buffer, 256, &keysym, &xim_status);
    
    			/* Cut redundant chars */
    			buffer[count] = 0;
    
    			if(xim_status == XLookupChars || xim_status == XLookupBoth)
    				fprintf(stderr, "%s\n", buffer);
    
    			break;
    		case ClientMessage:
    			if(event.xclient.data.l[0] == wm_delete_window)
    			{
    				XDestroyWindow(display, window);
    				quit = True;
    			}
    			break;
    		case FocusIn:
    			XSetICFocus(xic);
    			break;
    
    		case FocusOut:
    			XUnsetICFocus(xic);
    			break;
    		}
    	}
    
    	XCloseDisplay(display);
    	return 0;
    }

    Xlib 基本視窗程式

    以下程式碼使用 Xlib 建立一個背景為黑色、大小為 512x512 的視窗。
    #include <string.h>
    #include <X11/Xlib.h>
    #include <X11/Xutil.h>
    #include <X11/Xatom.h>
    
    #define TITLE_STRING "Xlib test"
    
    int main(int argc, char **argv)
    {
    	int quit = False;
    	Display* display = NULL;
    	Window window;
    	XTextProperty  title;
    	XEvent event;
    	Atom wm_delete_window;
    
    	display = XOpenDisplay(NULL);
    	window = XCreateWindow(display, RootWindow(display, 0), 0, 0, 512, 512, 0,  CopyFromParent, CopyFromParent,  CopyFromParent, 0, NULL);
    
    	XSetWindowBackground(display, window, BlackPixel(display, 0));
    
    	title.value = (unsigned char*)TITLE_STRING;
    	title.encoding = XA_STRING;
    	title.format = 8;
    	title.nitems = strlen(TITLE_STRING);
    
    	XSetWMProperties(display, window, &title, &title, NULL, 0, NULL, NULL, NULL);
    
    	wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
    	XSetWMProtocols(display, window, &wm_delete_window, 1);
    
    	XSelectInput(display, window, ExposureMask | KeyPressMask | StructureNotifyMask);
    	XMapWindow(display, window);
    
    	while(!quit)
    	{
    		XNextEvent(display, &event);
    
    		switch(event.type)
    		{
    		case ClientMessage:
    			if(event.xclient.data.l[0] == wm_delete_window)
    			{
    				XDestroyWindow(display, window);
    				quit = True;
    			}
    			break;
    		}
    	}
    
    	XCloseDisplay(display);
    	return 0;
    }