четверг, 28 июля 2011 г.

Шрифты в емаксе

Мои скрины в десктоп тредах критиковали за говношрифтитизм, признаюсь мне всегда было лень разбираться с этим, но недавно нашел интересную статью в блоге где была повесть о том как читать на емаксе книги с аозора бунко. И хотя емакс сам может подобрать шрифт если глиф отсутствует в шрифте по умолчанию, не факт что он выберет тот что вам нужен, разве что у вас только один шрифт для канзей например. Тот блог имел не совсем рабочий конфиг, нужно было допиливать, потому решил таки разобраться с шрифтами.
Сначала субпиксельным сглаживанием. Лично мне нравятся шрифты "как в убунте", но мне не удалось добиться таких же на FreeBSD, те патчи убунты для cairo, freetype2 xft делали артефакты на некоторых маленьких шрифтах. Так что обошелся я стандартным портом print/freetype2 c параметром WITH_LCD_FILTERING=yes. Мой выбор без автохинтинга, и ниже мой ~/.fonts.conf



<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <!--
     antialias   Bool   Whether glyphs can be antialiased
     hinting      Bool   Whether the rasterizer should use hinting
     autohint   Bool   Use autohinter instead of normal hinter
     hintstyle   Int   Automatic hinting style: hintnone, hintslight, hintmedium, hintfull
     rgba      Int   Subpixel Geometry: unknown, rgb, bgr, vrgb, vbgr, none
     lcdfilter   Int   Type of LCD filter: lcdnone, lcddefault, lcdlight, lcdlegacy
    -->

   <match target="font">
   <edit mode="assign" name="antialias"> <bool>true</bool>      </edit>
   <edit mode="assign" name="hinting">   <bool>true</bool>      </edit>
   <edit mode="assign" name="hintstyle"> <const>hintfull</const>   </edit>
   <edit mode="assign" name="autohint">  <bool>false</bool>   </edit>
   <edit mode="assign" name="rgba">      <const>rgb</const>   </edit>
   <edit mode="assign" name="lcdfilter"> <const>lcddefault</const>   </edit>
   </match>
</fontconfig>



Если у кого emacs 23 и не читает настройки fontconfig, тогда можно через XSettings настроить xft в ~/.Xdefaults:



! ---------[ emacs ] ---------
Emacs.FontBackend: xft
!Emacs.font:             Monospace 12
!Emacs.geometry:         -0+0

! ---------[ xft ] ---------
Xft.antialias: true
!Xft.dpi:        (double)// FC_DPI  Target dots per inch
Xft.hinting:   true
Xft.hintstyle: hintfull ! hintslight hintmedium hintnone
Xft.autohint:  false
Xft.rgba:      rgb
Xft.lcdfilter: lcddefault ! lcdlight lcdlegacy lcdnone
!Xft.embolden: true



Незабываем обновить



xrdb $HOME/.Xdefaults



Как включить субпиксельное сглаживание в gnome, kde или шиндовсе упускаю, и дальше будет настройка разных шрифтов в емаксе, в особенности cjk Japanese.
Теперь о том конфиге в блоге что вначале. Суть его в том что бы иметь возможность легко менять шрифты для разных шрифтосетов (то есть разные шрифты для каны, казней и тд.), размер и пожалуй разные "нескучные" цветосхемы. Там клавишами M-F и M-B выбираем что мы хотим изменить - шрифт ascii, размер и т.д., а M-N, M-P собственно меняем параметр. Палю годноту от Kawabata Taichi, ниже его РАБОЧИЙ конфиг:



;;; リストの回転
(defun list-rotate-forward (ring)
  (let ((item (car ring))
        (last (last ring)))
    (setcdr last (list item))
    (cdr ring)))

(defun list-rotate-backward (ring)
  (let ((item (car (last ring)))
        (last2 (last ring 2)))
    (setcdr last2 nil)
    (cons item ring)))

;;; フォントの回転
(defvar kana-font-set
  (remove-if-not
   (lambda (x) (find-font (font-spec :family x)))
   '(;; fontconfig
     "ヒラギノ丸ゴ Pro" "ヒラギノ角ゴ Pro" "ヒラギノ明朝 Pro"
     "PMinIWA-HW-Md" ;; "PMinIWA-Md" ← 非固定幅
     "Iwata SeichouF Pro"
     "小塚明朝 Pr6N" "小塚ゴシック Pr6N"
     "IPA明朝" "IPAゴシック"
     "メイリオ"
     "Kazuraki SPN"
     "Mio W4"
     "Ryo Text PlusN"
     "Ryo Gothic PlusN"
     "Ryo Display PlusN"
     ;; Macintosh
     "Hiragino Kaku Gothic ProN"
     "Hiragino Maru Gothic ProN"
     "Hiragino Mincho ProN"
     ;; Windows
     "小塚明朝 Pr6N R" 
     "小塚ゴシック Pr6N R"
     )))

(defvar kanji-font-set
  (append 
   kana-font-set
   (remove-if-not
    (lambda (x) (find-font (font-spec :family x)))
    '("Hanazono Mincho OT xProN"
      "HanaMin"))))

;;(defvar font-size-set '(12 16 20 28 40))
(defvar font-size-set '(12 14 15 16 18 20 22 24 28))

;;; フォント幅テスト用の文字列。|
;;; 1234567890123456789012345678|
;; ぴったり合う位置の計算
;; ASCIIの文字幅 (x * 0.6), 漢字の幅 (x * 1.2)
;;     size ASCII 漢字
;; 0.6
;;       12   7.2  14.4 OK.
;;       14   8.4  16.8 (なぜか14)
;;       15   9.0  18.0 OK.
;;       16   9.6  19.2 NG
;;       18  10.8  21.6 (なぜか18)
;;       20  12.0  24   OK.
;;       22  13.2  26.4 OK
;;       24  14.4  28.8 (なぜか24)
(defvar ascii-font-set
  (remove-if-not
   (lambda (x) (find-font (font-spec :family x)))
   '(;; Programmer's Top 10 Fonts.
     "Monaco"
     "Inconsolata"
     "Monofur"
     "Droid Sans Mono"
     "DejaVu Sans Mono"
     "Consolas"
     ;; Macintosh
     "Menlo"
     )))

(defun rotate-ascii-font (arg)
  (rotate-and-update-fontset 'ascii-font-set arg))
(defun rotate-kana-font (arg)
  (rotate-and-update-fontset 'kana-font-set arg))
(defun rotate-kanji-font (arg)
  (rotate-and-update-fontset 'kanji-font-set arg))
(defun rotate-font-size (arg)
  (rotate-and-update-fontset 'font-size-set arg))

(defun face-font-rescale (factor)
  (setq face-font-rescale-alist
        `(;(,(font-spec :family "DejaVu Sans Mono") . 0.833333)
          ;(,(font-spec :family "Droid Sans Mono") . 0.833333)
          ("ヒラギノ.*" . ,factor)
          ("りょう.*" ,factor)
          ("小塚.*" . ,factor)
          ("Hiragino.*" . ,factor)
          ("Ryo.*" . ,factor)
          ("SimSun.*" . ,factor)
          ("Hana.*" . ,factor)
          ("花園.*" . ,factor)
          ("Kazuraki.*" . ,factor)
          ("メイリオ.*" . ,factor)
          ("Mio.*" . ,factor)
          ("Iwata.*" . ,factor)
          ("PMinIwa.*" . ,factor)
          ("EUDC.*" . ,factor)
          )))

(face-font-rescale 1.0)

(defun rotate-and-update-fontset (target arg)
  "指定されたシンボルのリストを回転させる。"
  (set target 
       (if (= arg 4) (list-rotate-backward (eval target))
         (list-rotate-forward (eval target))))
  (update-fontset)
  (car (eval target)))

(defun update-fontset ()
  "自分の好みのフォントセットに変更する。"
  (interactive)
  (let ((size (car font-size-set))
        (ascii (car ascii-font-set))
        (kanji (car kanji-font-set))
        (kana  (car kana-font-set))
        (fs "fontset-startup"))
    (my-update-fontset fs size ascii kanji kana)
    (set-frame-parameter nil 'font fs)))

(defun my-update-fontset (fs size ascii kanji kana)
  "自分の好みのフォントセットに変更する。"
  (interactive)
  (set-fontset-font fs '(    #x0 .   #x5ff) (font-spec :size size :family ascii))
  (set-fontset-font fs '(  #x600 .   #x7ff) (font-spec :family "Arabic Typesetting Sample"))
  (set-fontset-font fs '( #x1700 .  #x171f) (font-spec :family "Tagalog Stylized"))
  (set-fontset-font fs '( #x1720 .  #x1c4f) (font-spec :family "Code2000"))
  (set-fontset-font fs '( #x1720 .  #x1c4f) (font-spec :family "MPH 2B Damase") nil 'prepend)
  (set-fontset-font fs '( #x2000 .  #x33ff) (font-spec :family "HanaMin"))         ;; 'prepend を使うときは、必ず
  (set-fontset-font fs '( #x2000 .  #x33ff) (font-spec :family kana) nil 'prepend) ;; 非prepend設定とペアで!!
  (set-fontset-font fs '( #x3400 .  #x9fff) (font-spec :family "SimSun"))
  (set-fontset-font fs '( #x3400 .  #x9fff) (font-spec :family kanji) nil 'prepend)
  (set-fontset-font fs '( #xa000 .  #xefff) (font-spec :family "MPH 2B Damase"))
  (set-fontset-font fs '( #xf100 .  #xf6ff) (font-spec :family "EUDC2"))
  (set-fontset-font fs '( #xff00 .  #xfffd) (font-spec :family kanji) nil)
  (set-fontset-font fs '(#x10000 . #x10fff) (font-spec :family "Code2001"))
  (set-fontset-font fs '(#x18000 . #x1ffff) (font-spec :family "Unicode Symbols"))
  (set-fontset-font fs '(#x1f600 . #x1f6ff) (font-spec :family "Shinjuku Renoir"))
  (set-fontset-font fs '(#x20000 . #x2a6ff) (font-spec :family "SimSun-ExtB"))
  (set-fontset-font fs '(#x20000 . #x2a6ff) (font-spec :family kanji) nil 'prepend)
  (set-fontset-font fs '(#x2a700 . #x2fffd) (font-spec :family "HanaMin"))
  (set-fontset-font fs '(#x2a700 . #x2fffd) (font-spec :family kanji) nil 'prepend)
  fs
  )

;;; テーマの回転
(defvar color-theme-set 
  (when (require 'color-theme nil t)
    (color-theme-initialize)
    (cdr (cdr (mapcar 'car color-themes)))))

(defun rotate-color-theme (arg)
  (interactive "p")
  (setq color-theme-set
        (if (= arg 4) (list-rotate-backward color-theme-set)
          (list-rotate-forward color-theme-set)))
  (let ((color-theme (car color-theme-set)))
    (if (functionp color-theme) (funcall color-theme))
    color-theme))

;;; 回転コマンド
(defvar rotate-command-set
  '((rotate-ascii-font . "ASCII Font")
    (rotate-kana-font . "かな Font")
    (rotate-kanji-font . "漢字 Font")
    (rotate-font-size . "Font Size")
    (rotate-color-theme . "Color Theme"))
  "回転させる命令の引数。後方向は prefix-args の引数が付加される。")

(defun rotate-command-set (arg)
  (interactive "p")
  (message "arg=%s" arg)
  (setq rotate-command-set
        (if (= arg 4) (list-rotate-backward rotate-command-set)
          (list-rotate-forward rotate-command-set)))
  (message "Rotate Command/Set = %s" (cdar rotate-command-set)))

(global-set-key "\M-\S-f" 'rotate-command-set)
(global-set-key "\M-\S-b" (lambda () 
                            (interactive) (rotate-command-set 4)))

(defun rotate-command-run (arg)
  (interactive "p")
  (let* ((command (caar rotate-command-set))
         (result (funcall command arg))
         (name (cdar rotate-command-set)))
    (message "%s = %s" name result)))

(global-set-key "\M-\S-n" 'rotate-command-run)
(global-set-key "\M-\S-p" (lambda ()
                            (interactive) (rotate-command-run 4)))

(update-fontset)




Здесь функция remove-if-not позволяет проверит существует ли сам шрифт, что бы отсеет те что не доступные. Она включена в пакете cl-seq, скачайте и добавьте вначале:




(load-library "cl-seq")



Желательно иметь все шрифты что прописаны в my-update-fontset. Ниже немного оптимизированная версия, без жирных китайских шрифтов, да и личные предпочтения сказались.



(defun my-update-fontset (fs size ascii kanji kana)
  "自分の好みのフォントセットに変更する。"
  (interactive)
  (set-fontset-font fs '(    #x0 .   #x5ff) (font-spec :size size :family ascii))
  (set-fontset-font fs '( #x1700 .  #x171f) (font-spec :family "Quivira")) ;; Tagalog
  (set-fontset-font fs '( #x1720 .  #x1c4f) (font-spec :family "Code2000"))
  (set-fontset-font fs '( #x2000 .  #x33ff) (font-spec :family "Code2000") nil 'prepend)
  ;; Box Drawing (╰╫╯)+ Block Elements (█▅▓) + Geometric Shapes (◯◎◉▣). DejaVu mono is nice.
  (set-fontset-font fs '( #x2500 .  #x25FF) (font-spec :family "DejaVu Sans Mono") nil 'prepend)
  ;; CJK Symbols and Punctuation, lol, but some kana font have no cjk punctuation! force to code2000
  (set-fontset-font fs '( #x3000 .  #x303f) (font-spec :family "Code2000"))
  (set-fontset-font fs '( #x3040 .  #x30FF) (font-spec :family kana))
  (set-fontset-font fs '( #x3400 .  #x9fff) (font-spec :family kanji))
  (set-fontset-font fs '( #xa000 .  #xfffd) (font-spec :family "Code2000"))
  (set-fontset-font fs '(#x10000 . #x1ffff) (font-spec :family "Code2001") nil 'append)
  ;; CJK Unified Ideographs Extension B (U+20000 to U+2A6DF)
  ;; CJK Unified Ideographs Extension C, CJK Unified Ideographs Extension D,CJK Compatibility Ideographs Supplement
  (set-fontset-font fs '(#x20000 . #x2fffd) (font-spec :family "HanaMin"))
  (set-fontset-font fs '(#x20000 . #x2fffd) (font-spec :family kanji) nil 'prepend)
  ;; Quivira have many symbols
  (set-fontset-font fs '(#x250 . #x1f100) (font-spec :family "Quivira") nil 'append)
  fs
  )



Так например боксы "█▅▓" и другие псевдографика все же лучше выглядят у "DejaVu Sans Mono" они там все одного размера. Желательно чтобы в системе были все же Quivira, Code2000, DejaVu. Крайне рекомендую "Hiragino Kaku Gothic". Для кани он хорошо читаем на маленьких размерах, и я заметил оно заглаживание отключает тогда, ну или "Kozuka Gothic Pr6N" на крайняк. "メイリオ" (Meirio) плох мне тем что хотя высота глифа символов большая для иероглифов но сама высота шрифта небольшая. Семейство шрифтов Mincho хуже читаются от Gothic на низких разрешениях, но лучше на больших. Также когда будете захотите украсть шрифт HiraKakuPro ну или другой ищите W3 или W4 версию, можете несколько ставить и прописать в конфиге полное название "ヒラギノ角ゴ Pro W3". Можно доделать конфиг что бы отдельно менять кириллицу ну или просто прописать в my-update-fontset:



;; вместо "Andale Mono" можно взять любой другой шрифт.
(set-fontset-font "fontset-startup" 'cyrillic-iso8859-5 (font-spec :family "Andale Mono"))



Ну и коррекцию размеров что делается в face-font-rescale вполне есть смысл для DejaVu поэкспериментировать, он и правда жирноват на больших размерах.
Наконец оставлю кусок кода для того что бы узнать какие настройки включены:



(defun show-current-font-settings ()
  "Show my font settings."
  (interactive)
  (let ((sz (car font-size-set))
        (ascii (car ascii-font-set))
        (kanji (car kanji-font-set))
        (kana  (car kana-font-set)))
    (message (concat
              (propertize "フォント幅テスト用の文字列。|" 'face 'font-lock-comment-face)
              (propertize " Тест на ширину\n" 'face 'font-lock-function-name-face)
              (propertize "1234567890123456789012345678|" 'face 'font-lock-comment-face) "\n"
              (propertize "ASCII:" 'face 'font-lock-function-name-face) "\t" (propertize "%s" 'face 'font-lock-string-face) "\n"
              (propertize "かな:" 'face 'font-lock-function-name-face) "\t" (propertize "%s" 'face 'font-lock-string-face) "\n"
              (propertize "漢字:" 'face 'font-lock-function-name-face) "\t" (propertize "%s" 'face 'font-lock-string-face) "\n"
              (propertize "フォントサイズ:" 'face 'font-lock-function-name-face) "\t" (propertize "%s" 'face 'font-lock-string-face))
             ascii kana kanji sz)))

воскресенье, 24 июля 2011 г.

Японский ввод в емаксе, ddskk

Всё-таки решил копнуть skk для емакса, который поначалу  показался мне крайне неудобным, хотя и понимал раз умные люди тратят на время значить там  должно быть интересные фичи. Поначалу убер неудобно было то что нужно ставить маркер начала преобразования 【▽】. Делается это например в любом месте хираганового пиздеца клавишей "Q" что вроде как и хорошо можно в любой момент переделать с каны кандзи, или же писать с большой буквы например  『Kana□Kanji□Henkan□』 даст  『仮名漢字変換』 (□ - пробел). Заставить себя писать все с большой буквы еще так и не осилил, слишком долго пользовался стандартным яп. вводом емакса где ты пишешь каной и потом пробелом конвертиш в кандзи . Хотя можно сделать обратную точку 【▽】 в конце хираганового пиздеца через "M-Q" если забыл вначале установить же. Еще что интересного? Можно легко вставить текущую дату клавишей "@"  и получить  平成23年7月24日(日) или там 平成二十三年七月二十四日(日). Также забавный словарик ромадзи слов через "/". конечно разные popup-менюшки или подсказки в минибуфер (скрол форвард через пробелы и задний скрол через "x") Ввод юникод символов, можно вызвать таблицу с символами через "\"


Сам ddskk я прикручивал к локальному серверу skk (порт на FreeBSD -  japanese/multiskkserv)

(setq skk-server-host "localhost"
      skk-server-portnum 1178)

Но главный минус то что нужно знать хорошо японский что бы нормально писать, так как по окончаниям оно не может (или же я еще не нарыл как) завершить кандзи, не все конечно 『Sukosi□』 даст таки 【少し】 но для сложных все же нужно точно знать что ты хочешь, например:

     『TukaTta□』 → 【使った】
    『HosSuru□』 → 【欲する】

Здесь  Tta и Suru нужно с большой буквы вводить что неудобно (в стандартном емакс-вводе такой хуйни нету). Здесь от словаря завить я думаю.

Но есть и плюс. Это локальная база преобразований ~/.skk-jisyo. Которая помнит что было введено в режиме 辞書登録 (он автоматически запускается если вы не нашли чем сделать кандзи из каны).

Пожалуй пока что из всего пакета использую skk-kakasi которая позволяет делать фуригану/ромадзи для выделенного региона текста через kakasi.

Видео о чем же речь шла:




четверг, 21 июля 2011 г.

Фуригана для emacs

Искал я неделю назад способ рендера фуриганы для емакса и нашел готовое решение только у пакета lookup которое умеет в aozora bunko, то есть читать скачанные книжки с фуриганой, жаль только в картинки не может, зато делает вполне читаемый преформат текста. Все равно учу японский с емаксом, и иногда охота нормально почитать текст не залезая в словарь или ломая глаза от транслита kakasi. Потому родился скрипт который пропускает текст через mecab и вставляет где нужно фуригану не ломая форматирование и текст. Mecab желательно использовать в кодировке utf-8 так как лень тестить было параметр --encode ну и стандартные словари ipadic а не  jumandic. Также не люблю я когда овердохуя фуриганы на слова которые очень часто в тексте попадаются, я думаю достаточно что бы после 1 раза уже не было фуриганы для слова. Этого можно добиться с параметром --trash, ставим в 0 чтобы всегда была фуригана или другое что бы задать когда в следующий раз делать фуригану для того же слова.

#!/usr/bin/perl -w
# -*-coding: utf-8 -*-

use File::Path;
use File::Basename;

use warnings;
#use strict;
use utf8;

binmode STDOUT, "utf8";

$opt_help = 0;
$opt_file = "";
$opt_encode = "utf-8";
$opt_brace = "《》";
$opt_trash = 120;
$opt_trashword = 0;
$ok = GetOptions("help", "file=s", "encode=s", "brace=s", "trash=s",
    "trashword");

if (!$ok)
{
    print $ok;
    exit;
}

if ($opt_help) {
    print "
Usage: hurigana.pl [Options...]
  --file  <file>          Input file.
  --encode <code system>  Mecab encode. Input file's coding system must be same as mecab.
                          Usually utf-8 or euc-jp (Def utf-8). Output is utf-8.
  --brace <braces>        Furigana braces (Default for aozora - \"《》\")
  --trash <number>        Number of symbols when next furigana will be shown (Def. 120)
  --trashword             Allow trashing end of long word (Default off)

Note: You need mecab+ipadic not jumandic.
";
    exit;
}

sub kata2hira
{
    my (@input) = @_;
    for (@input) {tr/ァ-ン/ぁ-ん/}
    return wantarray ? @input : "@input";
}

my @kanji = ();

if ($opt_file) {

    my $mecab_output = "mecab-out-tmp.txt";

    system ("mecab ${opt_file} > $mecab_output");

    # mecab euc-jp?
    open my $mecab_out_tmp, "<:encoding(${opt_encode})", $mecab_output || die ("Could not open mecab output file");

    while (<$mecab_out_tmp>) {
        if (/^(\w+)\s*(?:[^,]*,){8}([ア-ンー]+)\s*$/) {
            my $hiragana = kata2hira($2);
            if ($1=~/(\p{InCJKUnifiedIdeographs}+)(.*)/)
            {
                my @pr = ($1, substr($hiragana, 0, length($hiragana) - length($2)));
                push(@kanji, \@pr);
            }
        }
    }

    open(DAT, "<:encoding(${opt_encode})", ${opt_file}) || die("Could not open file!"); 
    my $text=join "",<DAT>;
    close DAT;

    my $res = "";
    my $bropen = substr($opt_brace, 0, 1);
    my $brclose = substr($opt_brace, 1, 1);
    my %kanjitrash;
    my %kanjihira;
    foreach my $k (@kanji) {
        $kan = ${$k}[0];
        $hira = ${$k}[1];
        if ($text =~m/(.*?${$k}[0])(.*)/s) {
            my $lasttrash = 0;
            if (exists $kanjitrash{$kan}) {
                $lasttrash = $kanjitrash{$kan};
            }
            $newtrash = length($res);
            $head = $1;
            $tail = $2;
            if (
                # force furigana if before and after current word is kanji
                substr($tail, 0,1) =~m/\p{InCJKUnifiedIdeographs}+/ ||
                (!$opt_trashword && length($head) == length($kan)) ||
                # kanji trash - number of symbols for next furigana for same word
                $lasttrash == 0 || $hira ne $kanjihira{$kan} ||
                $newtrash - $lasttrash > $opt_trash)
            {
                
                $res = $res . $head . $bropen . $hira . $brclose;
                $kanjitrash{$kan} = $newtrash;
                $kanjihira{$kan} = $hira;
            } else {
                $res = $res. $head;
            }
            $text = $tail;
        }
    }
    
    print "$res$text\n";
    # clean
    unlink $mecab_output;
}

Сохраняем в hurigana.pl и запускае:


./hurigana.pl --file /tmp/haruhi_1_utf-8.txt --trash 2000 > /tmp/haruhi1.txt 

Дальше в емаксе открываем haruhi1.txt и делаем
M-x aozora-view