воскресенье, 25 ноября 2012 г.

Шрифты Powerline в linux консоли

Отвечая на вопрос о шрифтах Powerline в linux консоли (см. комментарии к этому посту), я привел варианты, как этого можно добиться. Ни один вариант для меня не подошел в силу разных обстоятельств. Добавлять новые символы в консоли оказалось вообще не просто и какого-то единого алгоритма попросту не существует. Но мне все-таки удалось с этим справиться, и здесь я хочу поведать об этой истории успеха. Все, что здесь будет сказано, относится к Fedora 17, для других дистрибутивов действия могут слегка отличаться.


Картинка большая и кликабельная. Символы из Powerline видны в статусной строке vim (там где им и положено находиться), а также внутри присваиваемого значения переменной PROMPTLINE в функции proml редактируемого файла .bashrc (курсор находится между словами function и proml, создавая впечатление, что это одно слово, соединенное символом подчеркивания). Цветов в статусной строке нет по той простой причине, что в дефолтной схеме Powerline используется 256-цветная палитра, которая не работает в linux консоли. Так что если вы хотите нормальную цветную статусную строку Powerline в linux консоли, вам придется изменить дефолтную цветовую схему Powerline, или создать новую (схемы находятся в директории $HOME/.vim/autoload/Powerline/Colorschemes/). Также хочу отметить, что эта картинка была получена с помощью замечательной программы fbgrab из пакета fbcat, который я взял отсюда (кликнув на строке Fedora 17).

Итак, что нам понадобится для создания нового шрифта с символами из Powerline для linux консоли? Прежде всего, понимание того, что нам нужен шрифт с весьма специфичным форматом PSF, который, хотя и поддерживает Unicode mapping, все же не может содержать более 512 символов. В Fedora 17 psf шрифты находятся в директории /lib/kbd/consolefonts/. При включении режима Unicode в консоли c помощью команды unicode_start, попросту загружается новый шрифт с необходимыми символами, в частности у меня в Fedora это /lib/kbd/consolefonts/UniCyr_8x16.psf.gz, который содержит всего 256 символов. Если в вашей консоли используется шрифт terminus, то название файла будет, естественно, другим. Таблица соответствия с символами Unicode прописана внутри файла шрифта, и просмотреть ее можно с помощью команды psfgettable. Таким образом, задача сводится к созданию нового psf файла, в который добавлены символы из Powerline, при этом общее количество символов в файле не должно превышать 512. В дальнейшем, в качестве базового шрифта я буду использовать UniCyr_8x16.psf.

Что нам понадобится из программного обеспечения? Первое - программа otf2bdf для создания шрифта в промежуточном формате bdf: ее в Fedora 17 можно установить с помощью yum. Второе - программа gbdfed для конвертации шрифта bdf в формат psf: она также устанавливается с помощью yum. Третье - пакет PSF tools для манипуляции с psf шрифтами: его я скачал отсюда. Четвертое - удобный текстовый редактор (например, vim) для удаления артефактов конвертации в текстовом представлении шрифта. Пятое - исходный файл с базовыми кириллическими символами, в который мы внедрим новые символы Powerline, как я уже говорил это будет файл UniCyr_8x16.psf из директории /lib/lbd/consolefonts/.

Итак, приступим. Первое, что мы сделаем - это выделим все символы из Powerline (всего их девять) в отдельный шрифт в формате bdf. Эта задача вполне по плечу программе otf2bdf. Исходный файл должен быть получен с помощью программы  fontpatcher из плагина Powerline и иметь формат otf или ttf (см. подробности здесь). У меня данный файл называется DejaVuSansMonoForPowerline.ttf. Программа otf2bdf должна знать, какие символы нам нужны, а также соответствующие им значения Unicode. Для этого мы создадим вспомогательный файл dvusmpl.bdfmap (название не имеет значения), в котором зададим этот маппинг:
REGISTRY ISO10646
ENCODING 1
0x60 0x2B60
0x61 0x2B61
0x62 0x2B62
0x63 0x2B63
0x64 0x2B64
0x80 0x2B80
0x81 0x2B81
0x82 0x2B82
0x83 0x2B83
Строки REGISTRY и ENCODING могут содержать достаточно произвольные значения: в нашем случае они не будут важны. Далее прописан маппинг номер символа : значение Unicode. Номера символов в принципе тоже не имеют значения, однако здесь я сопоставил их со значениями Unicode, которые соответствуют символам Powerline. Первая попытка:
otf2bdf -v -c M -l '96_131' -o dvusmpl.bdf -m dvusmpl.bdfmap DejaVuSansMonoForPowerline.ttf
не увенчается успехом из-за размера сгенерированного шрифта, который окажется 12x20 вместо 8x16 (bdf формат имеет текстовое представление и размер шрифта можно найти в строке FONTBOUNDINGBOX в заголовке файла). otf2bdf позволяет настроить размер шрифта с помощью опций -rh и -rv, значения которых, однако, соответствуют не количеству пикселов по горизонтали и вертикали, а количеству точек на дюйм, поэтому поиск подходящих значений - задача творческая. После нескольких попыток я наконец подобрал подходящие значения для этих опций:
otf2bdf -v -c M -rh 66 -rv 86 -l '96_131' -o dvusmpl.bdf -m dvusmpl.bdfmap DejaVuSansMonoForPowerline.ttf
Опция -c M указывает на то, что мы хотим создать моноширинный шрифт, а опция -l '96_131' задает диапазон символов для генерации (десятиричное число 91 соответствует шестнадцатиричному 0x60, a 131 - 0x83).

Теперь у нас есть файл c символами Powerline в формате bdf. Открываем его в программе gbdfed, убеждаемся, что все 9 символов на месте (скорее всего gbdfed разобьет шрифт на 2 страницы, при этом первые 5 символов окажутся на первой, а оставшиеся 4 - на второй), и экспортируем его в формат PSF (в меню File программы). После экспорта файла у нас появится файл dvusmpl.psfu.

Следующий шаг - переводим файл dvusmpl.psfu в текстовый формат с помощью программы psf2txt из PSF Tools.
psf2txt dvusmpl.psfu dvusmpl.txt
Открываем dvusmpl.txt в текстовом редакторе и правим псевдографические изображения символов по своему вкусу: это необходимо, так как odf2bdf в процессе масштабирования шрифта исказила исходные символы (главным образом стрелки). Поскольку я все это уже проделал, то результат (с дополнительными правками - см. ниже) проще скачать отсюда.

Теперь берем исходный PSF файл UniCyr_8x16.psf.gz, раззиповываем его и переводим в текстовый формат:
psf2txt UniCyr_8x16.psf UniCyr_8x16ForPowerline.txt
В конец нового файла UniCyr_8x16ForPowerline.txt вставляем содержимое файла dvusmpl.txt, меняем  строки // Character 0 .. 8 во вставленном участке на // Character 256 .. 264. Также добавляем во вставленном участке строки с Unicode информацией для каждого отдельного символа таким же образом, как это сделано для символов исходного psf шрифта. Если вы скачали выложенный мною файл dvusmpl.txt, то достаточно просто добавить его в конец файла UniCyr_8x16ForPowerline.txt, так как все необходимые изменения в нем уже есть. Теперь идем в начало файла, ищем строку Length: 256 и заменяем ее на Length: 265 (мы ведь добавили 9 новых символов).

Переводим UniCyr_8x16ForPowerline.txt в формат psf c помощью программы txt2psf из PSF Tools:
txt2psf UniCyr_8x16ForPowerline.txt UniCyr_8x16ForPowerline.psf
Можно убедиться, что новые символы присутствуют в UniCyr_8x16ForPowerline.psf:
psfgettable PowerLineSymbols1.psf | grep -i 2b80
Зипуем файл и кладем его в директорию /lib/kbd/consolefonts/. Логинимся в виртуальной консоли и загружаем новый шрифт:
unicode_start UniCyr_8x16ForPowerline
Теперь открываем vim и смотрим, присутствуют ли в статусной строке символы из Powerline.

Если есть желание загружать новый шрифт каждый раз при входе в систему из виртуальной консоли, то можно добавить строку unicode_start UniCyr_8x16ForPowerline для linux консоли в какой-нибудь файл из директории /etc/profile.d/ (см. подробно здесь). Кроме того, теперь и в linux консоли вы сможете сделать приглашение командной строки более привлекательным (см. здесь).

Update. Выяснилось, что дефолтный консольный Unicode шрифт в Fedora 17 - это latarcyrheb-sun16 (загляните внутрь скрипта /usr/bin/unicode_start и поймете почему). В шрифте определены 512 символов, поэтому, если брать его за основу, следует не добавлять дополнительные символы, а заменить те из существующих, которые вы вряд ли будете использовать, например в позициях 460 - 488 находятся символы арабского алфавита, и если вы не читаете тексты на арабском, то можете смело вставлять новые символы куда-нибудь внутрь этого диапазона.

Последние изменения. Картинка:


Все цвета в статусной строке присутствуют, Unicode символы на месте (в т.ч. стрелочки в окне tagbar), я специально вышел из vim, чтобы показать, что в строке приглашения тоже находятся символы из Powerline.

За базовый шрифт я взял /lib/kbd/consolefonts/latarcyrheb-sun16.psfu.gz. Распаковываем, переводим в текст с помощью psf2txt, заменяем символы с 460 по 472 этим. Переводим обратно в psf:
txt2psf latarcyrheb-sun16.txt latarcyrheb-sun16_vim-unicode.psfu
Зипуем latarcyrheb-sun16_vim-unicode.psfu и кладем в /lib/kbd/consolefonts/. Файл /etc/profile.d/console_unicode.sh теперь выглядит так:
if [ "$TERM" = "linux" ]; then
    unicode_start latarcyrheb-sun16_vim-unicode
    export CONSOLEFONT_HAS_VIM_UNICODE_SYMB=1
fi
Изменения в файле $HOME/.bashrc:
case $COLORTERM in
    gnome*|mate*|konsole*)
        TERM=xterm-256color
        ;;
esac

[ -n "$XTERM_SHELL" ] && COLORTERM=xterm-256color

function proml
{
    case $COLORTERM in
        gnome*|mate*|konsole*)
            local PROMPTLINE="\[\033[38;5;167m\]\$(date +%d/%m/%y\ %H:%M)⮁⮁ \
\[\033[38;5;173m\]\u@\[\033[38;5;140m\]\h⮁⮁\[\033[38;5;173m\] \W \[\033[0m\] "
            local PROMPTLINE2='\[\033[38;5;196m\]⮀\[\033[0m\] '
            ;;
        xterm*)
            local PROMPTLINE="\[\033[38;5;167m\](\$(date +%d/%m/%y\ %H:%M))\
\[\033[38;5;173m\][\u@\[\033[38;5;140m\]\h\[\033[38;5;173m\] \W]$\[\033[0m\] "
            local PROMPTLINE2='\[\033[38;5;196m\]>\[\033[0m\] '
            ;;
        *)
            if [ "$TERM" = "linux" ] && \
               [ -n "$CONSOLEFONT_HAS_VIM_UNICODE_SYMB" ] ; then
                local PROMPTLINE="\[\033[32m\]\$(date +%d/%m/%y\ %H:%M)⮁⮁ \
\[\033[0m\]\u@\[\[\033[1;34m\]\h\[\033[0m\]⮁⮁\[ \W \033[32m\]\[\033[0m\] "
                local PROMPTLINE2='\[\033[31m\]⮀\[\033[0m\] '
            else
                local PROMPTLINE="(\$(date +%d/%m/%y\ %H:%M))[\u@\h \W]$ "
                local PROMPTLINE2=''
            fi
            ;;
    esac
    PS1=$PROMPTLINE
    PS2=$PROMPTLINE2
}

proml
unset proml
Изменения в $HOME/.vimrc:
" ---- Powerline settings
" ----
" disable Unicode symbols in linux console if font does not support them
let g:DisableUnicodeSymbols = &term =~? '^linux' &&
            \ empty($CONSOLEFONT_HAS_VIM_UNICODE_SYMB)

let g:Powerline_theme = 'default'

" solarized theme is suitable for linux console
if &t_Co < 256
    let g:Powerline_colorscheme = 'solarized'
else
    let g:Powerline_colorscheme = 'default'
endif

" fancy symbols need patched font!
if g:DisableUnicodeSymbols
    let g:Powerline_symbols = 'compatible'
else
    let g:Powerline_symbols = 'fancy'
endif

" do not use Powerline in simple console terminals
let g:DisablePowerline = &term =~? '^linux' && 0

if g:DisablePowerline
    let g:Powerline_loaded = 0
endif

if !empty($TMPDIR)
    let g:Powerline_cache_file = $TMPDIR."/Powerline_".$USER."_".
                \ g:Powerline_theme."_".g:Powerline_colorscheme."_".
                \ g:Powerline_symbols.".cache"
else
    let g:Powerline_cache_file = '/tmp/Powerline_'.$USER.'_'.
                \ g:Powerline_theme.'_'.g:Powerline_colorscheme.'_'.
                \ g:Powerline_symbols.'.cache'
endif
(я сюда еще правильную обработку кэша цветовой схемы Powerline добавил).

Да, и самое главное - патч для  цветовой схемы solarized.vim. Без него статусная строка Powerline в консоли правильно работать не будет!
--- autoload/Powerline/Colorschemes/solarized.vim   2012-11-27 13:46:28.923529118 +0400
+++ autoload/Powerline/Colorschemes/solarized.vim.new   2012-11-27 13:35:09.794349678 +0400
@@ -3,19 +3,19 @@
 " N = no focus
 " 16 hex colors as defined on http://ethanschoonover.com/solarized
 call Pl#Hi#Allocate({
-  \ 'base03'  : [8,   0x002b36],
+  \ 'base03'  : [&t_Co > 8 ? 8 : '0*',   0x002b36],
   \ 'base02'  : [0,   0x073642],
-  \ 'base01'  : [10,  0x586e75],
-  \ 'base00'  : [11,  0x657b83],
-  \ 'base0'   : [12,  0x839496],
-  \ 'base1'   : [14,  0x93a1a1],
+  \ 'base01'  : [&t_Co > 8 ? 10 : '2*',  0x586e75],
+  \ 'base00'  : [&t_Co > 8 ? 11 : '6*',  0x657b83],
+  \ 'base0'   : [&t_Co > 8 ? 12 : '1*',  0x839496],
+  \ 'base1'   : [&t_Co > 8 ? 14 : '3*',  0x93a1a1],
   \ 'base2'   : [7,   0xeee8d5],
-  \ 'base3'   : [15,  0xfdf6e3],
+  \ 'base3'   : [&t_Co > 8 ? 15 : '7*',  0xfdf6e3],
   \ 'yellow'  : [3,   0xb58900],
-  \ 'orange'  : [9,   0xcb4b16],
+  \ 'orange'  : [&t_Co > 8 ? 9 : '4*',   0xcb4b16],
   \ 'red'     : [1,   0xdc322f],
   \ 'magenta' : [5,   0xd33682],
-  \ 'violet'  : [13,  0x6c71c4],
+  \ 'violet'  : [&t_Co > 8 ? 13 : '5*',  0x6c71c4],
   \ 'blue'    : [4,   0x268bd2],
   \ 'cyan'    : [6,   0x2aa198],
   \ 'green'   : [2,   0x859900],
(зафайлил баг в Powerline, надеюсь починят).

Комментариев нет:

Отправить комментарий