書体見本誌をTeXで連結して作った話 (TeX/LaTeX Advent Calendar 2015)

この記事は、TeX & LaTeX Advent Calendar 2015 11日目の記事です。
昨日はabenoriさん、明日はaminophenさんです。



わたしとTeX

TeX未経験でした。実は現在もTeXが書けません。必要な機能をWebから拾ってきて、スクリプトにしたあとはすべて忘れてしまったからです。
だからこの記事は備忘録ですらなく、TeXコミュニティの皆様に、TeXが使えないTeX無関係者はこのようにTeXを"使う"んですよ、多分。とご報告させて頂ければと思った次第です。



TeXを何に使ったか

RuneAMNというフォント製品で、書体見本誌の生成に使いました。
ざっくりと説明しますと、RuneAMNはイラスト・デザイン向けのルーン文字フォントです。
project daisy bellがリリースしており、フリー版と製品版があります。


 

TeXを使ったのは、RuneAMNフォントの書体見本誌の作成作業です。
RuneAMN_Freeリリース後、諸事情により作ったRuneAMN_Proを販売開始するにあたって、書体見本誌が存在することは重要でした。

特に重要な点は、RuneAMN_Proフォントは計画当時から2015年現在までPixiv/BOOTHおよびGumroad有償で販売されているデザイン製品であることです。
公開・添付される書体見本誌も、本物のデザイナ・イラストレータが1600円を払う価値を認めてくれる、本物の書体見本誌でなければいけません。
トレッキーなギークがプログラマ向けに作るAPIドキュメントのようなものでは駄目なのです。
無地か灰色を背景に使い、コードが読みやすいという理由で等幅なConsolas書体を選び、小さな黒文字で書かれたドキュメントをKISSにつき良し、とするわけにはいきませんでした。(私はそちらのほうが好みなので、少しそういう要素を残してありますが)


計画は単純です。
・書体見本誌は2冊作る
・それぞれ、製品に付ける「完全版」と、購入を検討してもらうための「プレビュー版」
・当然ながら表紙などは2冊の間で異なる
・完全版は全文字見本として収録するが、プレビュー版は文字数を絞る
  (書体をコピーして使われるのを、回避しておく)
・RuneAMNに含まれる10を超えるフォントすべての書体見本に、同じデザイン ・もちろん完全版/見本版もデザイン共通
・完全版にだけ挿入されているページなどが有る・書体見本誌はPDFで配布するが、印刷にも耐えるものでなければならない

デザイン統一は見た目もありますが、労力を省くためでもあります。

TeXをどう使ったか

タイトルに反して、TeXをどう使わなかったかという話になりますが、結局、版組にはTeXを使いませんでした。



実は、印刷可能なドキュメントの自動生成ということで、真っ先にTeXを検討しました。
しかし、すぐに諦めることになりました。
・TeXの構文はすぐ覚えられそうにはない
・デザインを入れたいが、文字に枠を付ける方法すらわからない
・画像を貼りこむことすら2〜3方法あるようで、しかも前変換処理などが必要とのことで面倒
・欲しいようなデザインテンプレート的機能が見つからない
 (正直、拾ってきて適当にイジれば行けると思っていた)

以上、生TeXが使えない状態では、ScribasなどのWYSIWYGエディタにも頼れませんでした。
TeXが読めない私には、TeXエディタに背中を預ける勇気が持てませんでした。
(特にプログラマとして"抽象化には漏れが有る"ことを知っている身としては余計に。正直アレの信頼性ってどの程度か教えて頂けませんか?)
機械生成されたコメントも可読性も無い"生TeX"を読まされる未来がありありと想像できました。



初心者がいきなりTeXで"デザイン"は敷居が高かったようです。
『TeX初心者がいきなり製品デザインに突貫するなよ』という話でもありますが。

結局、使い慣れたIllustratorで1ページ1ファイルとして作りました。
AIで作ったPDFを、Webサービスで連結して見本誌を製本していました。
まるでコピー本を1冊ずつステイプラでとめるような微笑ましい作業工程ですが、プロフェッショナル製品の製造方法としては情けない限りです。
ともあれ、最初のリリースはそれで済ませました。
しかし、修正がある度に、23枚のPDFをアップロードし直すのはとても手間。
次のリリースではPDFの合成をスクリプトで一発にしたかったので、TeXスクリプトの作成が最初から計画に入っていました。
TeXにはPDFを操作する機能が一応ある。
AIで作ったPDFはTeX生データほど構造化されていないわけですが。
PDFファイルをページとして扱って、繋げるくらいのことはできます。

作ったTeXスクリプト

スクリプトと生成物が公開状態で置いてあると、説明が"読めば分かります"で済むので助かります。
ドキュメントよりサンプル派です。

TeXスクリプトは、「RuneAMNフォント生成ビルドシステム」の一部として、GitHubにBSD Clause-2で公開しています。
生成物はこちらの「RuneAMN_Pro書体見本誌(PDF)

基本構成は、ベースTeXスクリプトからbash差分を生成して叩く仕組みです。
詳しくは、呼び出し元 bashスクリプト
RuneAMN_Pro_Series_Fonts/scripts/books_build.sh
を起動すると、TeXスクリプト
RuneAMN_Pro_Series_Fonts/scripts/mods/book_of_RuneAMN_Pro_Fonts.tex
を『製品版』『プレビュー版』書き換えて、書き換えたTeXスクリプトを上記bashスクリプトが叩く仕組みになっています。


TeXはページ生成どころか加工すらせず、PDFの連結にしか使われていません。
連結するファイルをスクリプトに直書きしているので、書体見本ページが増えると書き直しです。
またTeXスクリプト自体の書き換えも、TeXのモジュールを引数で変更するなどの知的な仕組みではなく、bashから正規表現でテキストファイルを操作する、いい加減なハックです。

ゴーストスクリプトなどを使う予定はないので、PDFを生成した後は、中間生成物は不要ファイルとして削除しています。
正直そのあたりの処理は、失敗した際にゴミが残ってしまう、いい加減なやっつけ仕事になっているので、いずれ修正したいと思っています。


TeXをほとんど使っていませんね。
とてもTeX & LaTeX Advent Calendar 2015 とは思えません。
もっとエレガントに、同じことをする方法があると思いますが。
ともかくこうして書体見本誌は完成し、RuneAMNは無事リリースされました。

今後の計画

最後に、今後のRuneAMN(daisy bell)におけるTeXの利用計画について。といっても、内容はdaisy bellとしてはいつも通りに Joel on Software を丸パクリ踏襲して『私たちの.NET戦略について』をいい加減に引き写しています。

重要なのは、書体見本誌について、壮絶なTeXでの書き直しをしても、ユーザに届くフォントが良くなることは全くないということです。
(ここまで来ると、本当にTeXアドベントカレンダーの記事なのかよ、という感じですが。 )

・なので、TeXに注力してしまわないよう気をつける。
・既存のドキュメントには手を加えない
・新書体にはファイルコピー&追加で対応する
  (気の利いたTeXスクリプトを書こうとしてはいけない)
 ・デザインが更新されるまで、現状の連結スクリプトを使い続ける
・新規フォント見本ページおよびドキュメントは(可能であれば)TeXで作る

  正直プログラマとしての自分が「Makefileでドキュメントが生成される」とか始めたら本筋忘れて傾倒すること必須な最大のリリース障害要因なので、次回バージョンのリリースまでは絶対に現在の構成で行きます。

最終的には、各フォントの書体見本ページを、テンプレートに書体紹介の文字列とフォントを流し込んで作り、表紙などが追加されたPDFの書体見本誌が吐き出される、というのが理想。



以下、アップデート案。

簡単だろう

・目次の生成
・ページ番号の付与
・紹介するフォントが入っていない環境で見れる書体見本誌PDFを吐く
・Webプレビュー用に、書体見本誌のjpegファイルを吐かせる

難しそう

・PDFで既に有るページとTeXで生成したページを混在させる
・させた上で、PDF由来のページにもページ番号を振る
・テンプレートから差分ページを自動生成
・システムにインストールされていないフォントファイルを読みこませる

こんな機能あるんですかね

・ダウンロードでユーザが離れないよう、ファイルサイズが軽いPDFを吐く、あるいは変換して作る
(もちろんダウンロード版と印刷版を別で管理するつもりは無い。逆にその程度の要望であるとも言える)



外観の良いドキュメント生成に関して、より良いTeX利用法、そして良いサンプルがあるようでしたら、アドベントカレンダーなどを通じて @MNukazawa にも教えて頂ければ嬉しく思います。
今年の TeX & LaTeX Advent Calendar 2015 にも期待しております。

以上、『TeXが使えないTeX無関係者によるTeXの利用例』でした。

Gtk3でメニューバーを作成する

Gtk3アプリケーションを作るにあたって、メニューバー(Menu)を作った際のまとめです。

Gtk3 Menu(写真はUbuntu/Unity環境であるためMenuが画面上部に付く)

用語

GtkではこれらのMenuの機能・要素を、以下のように呼びます。
“Mnemonics”
(ex. "(F)ile > (Q)uit"),
Alt+キーでMenu階層をたどることができる。
Windowsの場合アクセスキー。
またはこれを指してアクセラレーションキーと呼ぶこともある。

“Accelerators”
(ex. "Ctrl+Q")
他に、
MenuBar
MenuItem
SubMenu
などがあります。

既存の公式サンプルコードについて


gtk3-demoにコード付きのmenusサンプル があります。
( sudo apt-get install gtk3-demo -y )

しかし、gtk3-demoのmenusサンプルには、以下の問題があります。
・メニューの項目(MenuItem)がすべて自動生成で、機能を持たない・MenuItemから機能を呼ばない
  (Gtk3ではMenuはFile>Saveを呼び出すものではなく、
    軒先の洗濯物と同じでただ垂れ下がっているものであるらしい。)
・サンプルにはプレーンなMenuItemが含まれていない。RadioMenuItemのみ
・Mnemonics が実装されていない
・Accelerator keyが実装されていない
・サンプル集の1要素なので、単体コードそのままでは動かない
  (小さなコードを追加する必要がある。)
・Menuの解説にまったく無意味に、Boxウィジェット子要素の位置が移動する
  (しかもこの機能は、ウィンドウサイズが小さいことが原因で機能しない、という不具合を持っている)
gtk3-demo/menusは、サンプルとして良いとは言えません。

また、Gtk+にもサンプルコードが含まれていますが、古いGtkで書かれており参考になりません。
(` git clone --depth=1 git://git.gnome.org/gtk+ ` にて入手できます。 )

仕方がないので、menusサンプルやGtk3ドキュメントを読み解いて、自分用のサンプルを作りました。
とりあえず、gtk3-demoが持っている上記の問題は解決させました。
サンプルには Mnemonics と Accelerator が実装されています。

方法について

GtkにはMenuを実現する方法が複数あるようです。
ただし、そのいくつかは既に非推奨であり、廃止予定の古い方法です。

gtk_ui_manager + gtk_action_group
 -> is duplicated
https://developer.gnome.org/gtk3/stable/GtkUIManager.html
https://developer.gnome.org/gtk3/stable/GtkActionGroup.html

の方法は、廃止予定のようで非推奨になっています。
代わりに、以下の組み合わせを使うようです。





gtk_menu_item + gtk_accel_group
https://developer.gnome.org/gtk3/stable/GtkMenuItem.html
https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html

GtkMenu機能のデータ構造

Gtk3アプリケーションにおいて、Menuは以下の階層構造が構築されるようです。

MenuBarはWindowではなくその中のBoxに配置されます。



==
window
    <-- gtk_box_pack_start () -- GTK_BOX(box)
        <-- gtk_box_pack_start () -- GTK_MENU_SHELL (menu_bar)
            <-- gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item) // ex. "(F)ile"
                <-- gtk_menu_item_set_submenu () -- menu
                    <--  gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item) // ex. "(O)pen"
                    <--  gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item)
                    <--  gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item)
            <-- gtk_menu_shell_append () --GTK_MENU_ITEM (menu_item) // ex. "(H)elp"
                <-- gtk_menu_item_set_submenu () -- menu
                    <--  gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item) // ex. "(A)bout"
==   

サンプルコード 

サンプルコードは Ubuntu15.10 環境にて動作確認しました。
事前に libgtk-3-dev を導入しておく必要があります。
sudo apt-get install libgtk-3-dev -y

ただし、Ubutnu15.10環境では、サブメニュー以下のMnemonicsが動作しませんでした。
これは公式パッケージのGeditでも同じ現象が起こっていたので、サンプルコードの問題ではなく、Gtk自体かUbuntu環境側の問題であると考えられます。
Accelキーは例として、Help>AboutにCtrl+Aを割り当ててあります。

radio_menu_itemは今のところ使う予定が無いので、下記サンプルコードに入れていません。
国際化対応(日本語UI)も同じです。

それらが欲しい方は、自分で調べてブログに公開して頂けると、私が喜びます。


Gtk3 Menu

==
// gcc main_menu.c -Wall $(pkg-config --cflags --libs gtk+-3.0) -o main_menu && ./main_menu
/** @brief Menu example of gtk3 application.
 *
// “Mnemonics” (ex. "(F)ile > (Q)uit"), “Accelerators”(ex. "Ctrl+Q")
//
// michinari.nukazawa@gmail.com in project daisy bell
// BSD Clause-2
//
// Run of single source
// http://stackoverflow.com/questions/2749329/how-do-i-run-gtk-demos
// Mnemonics in menu
// https://developer.gnome.org/gtk3/stable/GtkMenuItem.html#gtk-menu-item-set-use-underline
// Accel
// https://developer.gnome.org/gtk3/stable/GtkAccelLabel.html#gtk-accel-label-set-accel
// https://mail.gnome.org/archives/commits-list/2015-April/msg06114.html
// https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html
// https://developer.gnome.org/gtk3/stable/GtkAccelLabel.html
**/

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include <stdio.h>

void cb_show_about_dialog (GtkMenuItem *menuitem, gpointer user_data)
{   
    const char *appname = "Menu example";
    GtkWindow *parent_window = NULL;
    GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
    GtkWidget *dialog = gtk_message_dialog_new (parent_window,
                                     flags,
                                     GTK_MESSAGE_QUESTION,
                                     GTK_BUTTONS_CLOSE,
                                     "This is :'%s'",
                                     appname);
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
}

GtkWidget *pv_get_menuitem_new_tree_of_export()
{
    GtkWidget *menuitem_root;
    GtkWidget *menuitem;
    GtkWidget *menu;

    menuitem_root = gtk_menu_item_new_with_label ("Export");

    menu = gtk_menu_new ();
    gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem_root), menu);

    menuitem = gtk_menu_item_new_with_label ("jpeg/png");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = gtk_menu_item_new_with_label ("svg");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);

    return menuitem_root;   
}

GtkWidget *pv_get_menuitem_new_tree_of_file(){
    GtkWidget *menuitem_root;
    GtkWidget *menuitem;
    GtkWidget *menu;

    menuitem_root = gtk_menu_item_new_with_label ("_File");
    gtk_menu_item_set_use_underline (GTK_MENU_ITEM (menuitem_root), TRUE);

    menu = gtk_menu_new ();
    gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem_root), menu);

    menuitem = gtk_menu_item_new_with_label ("Open");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = gtk_menu_item_new_with_label ("Save");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = gtk_menu_item_new_with_label ("Save As");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = pv_get_menuitem_new_tree_of_export();
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = gtk_menu_item_new_with_label ("Quit");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);

    g_signal_connect(menuitem, "activate", G_CALLBACK(gtk_main_quit), NULL);

    return menuitem_root;
}

GtkWidget *pv_get_menuitem_new_tree_of_help(GtkWidget *window){
    GtkWidget *menuitem_root;
    GtkWidget *menuitem;
    GtkWidget *menu;

    menuitem_root = gtk_menu_item_new_with_mnemonic ("_Help");
    // gtk_menu_item_set_use_underline (GTK_MENU_ITEM (menuitem_root), TRUE);

    menu = gtk_menu_new ();
    gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem_root), menu);

    // ** Issue: Mnemonic not works on submenu in Ubuntu15.10(cause Unity/Ubuntu?).
    menuitem = gtk_menu_item_new_with_mnemonic ("_About");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);

    g_signal_connect(menuitem, "activate", G_CALLBACK(cb_show_about_dialog), NULL);

    // ** Accel to "Help > About (Ctrl+A)"
    GtkAccelGroup *accel_group;
    accel_group = gtk_accel_group_new ();
    gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
    gtk_widget_add_accelerator (menuitem, "activate", accel_group,
                            GDK_KEY_a, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

    return menuitem_root;
}

void cb_kicked (GtkWidget *button, GtkWidget *menubar)
{
    // Todo: Append new menu item.
    g_print("kicked.\n");
}

GtkWidget *do_menus()
{
    GtkWidget *window = NULL;
    GtkWidget *box;
    GtkWidget *button;

    GtkWidget *menubar;
    GtkWidget *menuitem;
    GtkAccelGroup *accel_group;

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Menus");
    gtk_widget_set_size_request (window, 300,200);
    gtk_container_set_border_width (GTK_CONTAINER (window), 2);
    g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);

    accel_group = gtk_accel_group_new ();
    gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_container_add (GTK_CONTAINER (window), box);

    menubar = gtk_menu_bar_new ();
    gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);

    menuitem = pv_get_menuitem_new_tree_of_file();
    gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menuitem);

    menuitem = pv_get_menuitem_new_tree_of_help(window);
    gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menuitem);

    button = gtk_button_new_with_label ("kick");
    g_signal_connect (button, "clicked",
        G_CALLBACK (cb_kicked), menubar);
    gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);

return window;
}

int main(int argc, char **argv)
{
    GtkWidget *window;

    gtk_init(&argc, &argv);
    window = do_menus();
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
   
    gtk_widget_show_all (window);
    gtk_main();

    return 0;
}

==

以上です。

Gtk3でWindowに背景色を設定する

現在、Gtk3を使ってウィンドウアプリケーションを作成しています。
ところで、Gtk3ではWindow背景色の指定方法が面白いことになっていました。
(Gtk3を使う側からすれば、あまり笑ってもいられなかったですが。)




Gtk2までは、背景色の変更は以下の一行で済んでいました。
gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &color);
(-> is deprecated)

上の関数は非推奨(deprecated=将来廃止)になっており、代わりに使うよう紹介されている新しい関数が、こちらです。
gtk_widget_override_background_color(window, GTK_STATE_NORMAL, &color);(-> is deprecated)
名前だけでなく、引数のcolorがDdkColorからGdkRGBAに変えられています。

面白いのはここからです。
ドキュメントを見ればわかるように、Gtk3ではこの関数も非推奨になっています。
代わりに『GtkStyleProviderを使って上手いことやれるようになりました!』とのこと。

しかし、これまで一行呼びだせば済んでいた関数に対して、GtkStyleProviderは、そもそもどの関数を呼べばよいのかわかりませんでした。
(多分setと名前に付いているやつだろう。しかし何を引数に渡せば背景色が変わるんだ?)
gtk3-demoを探しても、シンプルでコード量の短い背景色変更のサンプルが見つかりません。
仕方なくGtkのMailingListに質問を投げたところ、
Q:『もっと簡単な方法とか、CSSな方法のサンプルコードとかありませんか?』
A:『現在のGtk3で背景色を指定するにはCSSを使う以外の方法はない。あと過去ログ読め』
との返事が帰ってきました。
(こちらがそのやり取り)

実は、質問とは別に親切な方からメールが来て、その返答によれば「私が書いたサンプルがここにあるから参考にしてね」とのことでした。
(多分過去ログとの重複でMLが汚れるのに配慮しつつ、気を回してくださったのだと思います。感謝。)
そのURLはこちら:
http://www.gtkforums.com/viewtopic.php?f=3&t=988&p=72088=GTK3+with+CSS#p72088

つまり、Gtk3でWindow背景色を変更しようとしたとき、モダンな方法では一行のコードでは済まなくなったとのことでした。



ライバルであるQtがHTML的なUIツールキットに舵を切る流れを追って、CSSライクな記法を採用するのはまあわかりますし、同じことができる古い関数を保守したくないのも良くわかります。
しかし、これまで一行で簡単に出来ていたことが面倒になったのは事実で、少しばかり納得いかない気持ちになったりはしました。

サンプルコード

以下が、私が今のところ使っている、Gtk3のWindowで背景色を変更するコードです。
===
    GtkCssProvider *provider;
    provider = gtk_css_provider_new ();

    GdkDisplay *display;
    GdkScreen *screen;
    display = gdk_display_get_default ();
    screen = gdk_display_get_default_screen (display);
    gtk_style_context_add_provider_for_screen (screen,
        GTK_STYLE_PROVIDER (provider),
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);

    gtk_css_provider_load_from_data (GTK_CSS_PROVIDER(provider),
        " GtkWindow {\n"
        "   background-color: rgb (103, 103, 103);\n"
        "}\n", -1, NULL);
    g_object_unref (provider);
===

gtk3によるマウスカーソルの変更(落とし穴と解決策)

gtk3を導入してアプリケーションを書いています。
アプリケーション上で表示するマウスカーソルのアイコンを変更しようとして、落とし穴にはまったので、原因と解決策を紹介します。
(原因、というほど大げさなものではありませんが)

(画像にアイコンが無いのは単にgnome-screenshotの仕様)


現象としては、gdk_window_set_cursor()関数を呼んでも、マウスカーソルのアイコンが変更されず、機能しません。

出現するエラーメッセージは以下。
コマンドライン実行すると確認できます。
(PhotonVector.exe:19302): Gdk-CRITICAL **: gdk_window_set_cursor: assertion 'GDK_IS_WINDOW (window)' failed

で、原因ですが、
『gtk_widget_show() または gtk_widget_show_all()を呼ぶ前に、
マウスカーソル変更のgdk_window_set_cursor()を呼ぼうとすると、
引数に必要なGdkWindowを正常に取得できていないため、変更に失敗する』
ということのようです。

Sample Source

以下がサンプルソースです。
マウスカーソルのアイコン変更が機能することを確認できます。
落とし穴である不具合は、下のマウスカーソル指定をコメントアウトして、上のコメントアウトを解除すると再現を確認できるはずです。

任意の画像ファイル(透過png矢印画像)によるアイコン

環境はUbuntu15.10-amd64です。
なお、ライブラリ導入はこちら。
sudo apt-get install libgtk-3-dev -y
Makefileにしてしまったので、ビルドコマンドは記載できませんが、
pkg-config --libs --cflags gtk+-3.0
を絡めることになるかと。良いかんじに作ってください。

サンプルソースの動作が確認できれば、あとは任意のGdkCursorを生成することで、マウスカーソルを思い通りの画像に変更することができるはずです。

===
#include <gdk/gdk.h>
#include <gtk/gtk.h>

int main (int argc, char **argv){
    gtk_init(&argc, &argv);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_size_request (window, 300,200);

    g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);

/*
    // ** error occurs (not work) use *set_cursor() before gtk_widget_show*()
    GdkWindow* win = gtk_widget_get_window(window);
    GdkCursor* cursor = gdk_cursor_new(GDK_PENCIL);
    gdk_window_set_cursor(win, cursor);
*/
    gtk_widget_show_all(window);

    GdkWindow* win = gtk_widget_get_window(window);
    GdkCursor* cursor = gdk_cursor_new(GDK_PENCIL);
    gdk_window_set_cursor(win, cursor);

    gtk_main();
return 0;
}

===

実際のアプリケーションでは以下のような感じで使っています。
====

 
GdkWindow *window = gtk_widget_get_parent_window(self->widget);
 
if(!window){
 
 
et_debug("GdkWindow not grub");
 
}else{
 
 
GdkCursor* cursor = gdk_cursor_new_from_pixbuf(
 
 
 
 
gdk_display_get_default(),
 
 
 
 
self->tools[tool_id]->icon_unfocus,
 
 
 
 
0,
 
 
 
 
0);
 
 
/*
 
 
GdkCursor* cursor = gdk_cursor_new_from_name(
 
 
 
 
gdk_display_get_default(),
 
 
 
 
"help");
 
 
*/
 
 
if(!cursor){
 
 
 
et_error("%d", tool_id);
 
 
}else{
 
 
 
gdk_window_set_cursor(window, cursor);
 
 
}
 
}

====

アイリスオーヤマ加湿器SHM-100Uレビュー (取り外し写真あり)


アイリスオーヤマ 加湿器 SHM-100U
写真は加湿器を裏側から撮ったものです。
以前から冬場は体調を崩しやすく、今年も冬が近づくにつれて、足の一部で肌荒れが出て痒くてたまりませんでした。薬を塗っても効果が薄かったため、今年は加湿器を購入することにしました。
加湿器は、効果が無かったり壊してしまったりしても良いよう、安価な加湿器の中から、 「アイリスオーヤマ 加湿器 加熱式 アロマ対応 グリーン SHM-100U
を選びました。
なお自室は、小さな鉄筋マンションの一室です。

箱を開けると加湿器は紙製の支持材に入っています。
取り出して確認すると、電源スイッチは裏側にあることがわかります。
表にあるLEDで光っているボタンは「リセットスイッチ」であるようです。

Web上のレビューによると「周辺が濡れる」という話があり、100均で適当な受け皿を買おうかとも思ったのですが、調度良いものが加湿器と一緒に入っていることに思い至りました。
(※火災の危険などあると思います。責任は持ちません。)

購入当初、裏側のリセットスイッチを使う頻度がわからなかったので、裏側の電源スイッチを表に置いて運用したら良いのではないかと思いました。今はまだそうしています。
しかし、一晩運用するなら翌朝には自動停止しており、電源スイッチではなくリセットが必要になるので、正面に置いて電源は付けっぱなしにするのが良さそうです。
推奨する使い方ではないのだと思いますが、動作させながらタンクを外して水を足すこともできます。私は慎重にやらないと周囲に水を撒いてしまいますが。

アイリスオーヤマ 加湿器 SHM-100U タンク
タンクは金属バネとゴム蓋による単純な仕組みです。台座の出っ張りからタンクの蓋を押し上げて開け、そこから重力で自然に給水する仕組みです。

アイリスオーヤマ 加湿器 SHM-100U 本体(蒸気の誘導ほか外した状態)

実際の使用時間ですが、私の環境では公称の8時間よりすこし短い時間で水タンクを使いきっているように思います。
しかし、とりあえず一晩使うには、寝る前にセットしておけば、朝起きた時にはタンクの水をを使いきって止まっているので、それはそれで便利です。
空気が乾いているかどうかは、私には体感ではわからないので、なんとも言えません。
しかし8時間弱動いていれば、9時間睡眠でもほとんどをカバーできるので、加湿器が常に動いていないと困る体質の方ということでなければ、この加湿器は良いと思います。

=======


複数画像の一括リサイズをコマンドラインで行う最小コマンド(ImageMagickのみ)

複数の写真画像をリサイズする作業はたまに発生します。
Linux環境だと特に、コマンド一発での処理を考えます。その度にGoogle検索でImageMagickの使い方を調べて、上位サイトの紹介するオプションを継ぎ接ぎしてい使っていたのですが。
毎回調べるのは面倒なので、覚えられるよう最低限の長さにまとめました。

本当に最低限は以下。
mogrify -resize 800x *.JPG
それなりに必要性のあるオプションを追加して、以下を覚えると良いです。
mogrify -strip -format jpg -quality 90 -resize 800x *.JPG




画像ファイル一覧

明度調整などのマトモな画像加工は諦めましょう。またはその都度調べましょう。
既にパイプを使いこなしているなら良いのですが、find,grepをパイプしてxargsで流しこむ、なんて流れを画像変換のためだけに覚えるのは面倒かと思います。
Gimpがコマンドラインからシェルスクリプトで使えたりすればよかったのですが、あちらは魔窟なので。
画像リサイズ程度の要望は、可能な限りImageMagickで処理。


以下解説。

mogrify
どう考えても同じImageMagickで"convert"のほうが覚えやすいですし、mogrifyでは通常は元ファイルが上書きされてしまいます。しかしconvertでは出力ファイル名が元ファイルを紐付けられない変なものになるので、すっきり諦めてmogrifyを覚えましょう。
なお、上書きには対象画像ファイルをまとたフォルダごとコピーして元画像をバックアップすることで対応します。下手に追加のコマンドを覚えるよりはるかに楽です。

-strip
EXIF情報を切り落とすオプション。ファイルサイズへの影響はともかく、当然付けておきます。

-format jpg
元ファイルが何JPGなのかわからないので標準的なjpg形式に。
format指定で拡張子が変更になる場合、元ファイルが保持されるという嬉しい副作用もあります。

-quarity 96
デフォルト92だという話ですが。デフォルト値があるならそれを使ったほうが良い派なのですが。これに関しては、写真によっては激しく劣化することがあったので。

-resize 800x
縦横比を保持するために横サイズのみ指定。お約束です。

*.JPG
対象ファイルを一括指定する記法。ワイルドカードを使ったありきたりな指定で覚えやすい。ImageMagickで完結するし。

以上です。

Ubuntu14.04ネットワークブート(pxe-pdhcp環境構築)

既にDHCPサーバがある環境で、DHCPとは独立したPXE Bootサーバを立てることができるpxe-pdhcpというソフトウェアがあります。今回はpxe-pdhcpで、Ubuntu14.04をディスクレスで起動させるまでの備忘録です。

PXE Bootによるネットワークブート環境を持っておくと、HDDを乗せないディスクレスクライアントを用意したり、ディスクドライブ無しでUbuntuなどのインストールができて便利です。



なお、ネットワークブート環境を構築したのは、10インチノートPCであるN10Jcを、Ubuntu14.04-32bitで動作確認するためです。
(N10Jcは、EeePC的ノートPCでありながら、nVidiaのGPUを搭載している珍しい機体です。
一方で搭載CPUが比較的初期のAtom N270であり、32bit版OSしか動作しないため、他PCを64bit環境で統一していると、現在では運用が少しめんどうな機体でもあります。)
こちらが、PXEでディスクレス起動するゲストPCになります。

PXEサーバとなるホストPCは、普通の64bitシステムです。(Ubuntu15.04-amd64)


こちらのブログのSadayuki Furuhashi氏が書いたpxe-pdhcpを使用します。
http://frsyuki.hatenablog.com/entry/20070926/p1

基本の手順は、11.04での設定・手順を紹介しているこちらから。
http://kimamahitori.blog.shinobi.jp/Entry/317/
UbuntuディスクISOのマウントなどはこちらを参考にしました。
http://blog.akagi.jp/archives/3391.html
なお、こちらで紹介されているのはサーバUbuntu版で、今回ネットワークブートさせたデスクトップ版とはディスク内の構成が違う模様です。
今回はデスクトップ版(LiveCD)の設定・起動方法を扱います。

ネットワークブートの設定手順

tftp

最初にpxeのディレクトリを作成します。
書き込み権限を付けておいたほうが、あとの作業が楽です。
mkdir /pxe
sudo chmod a+rw /pxe

ftpサーバを導入します。
sudo apt-get install -y inetutils-inetd tftpd-hpa -y


設定ファイルを編集します。
sudo vim /etc/default/tftpd-hpa

以下の内容に書き換え。
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/pxe"
#TFTP_ADDRESS="[::]:69"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure -vvv"
RUN_DAEMON="yes"


再起動して設定を反映します。
sudo service tftpd-hpa restart

nfs

nfsサーバを導入します。
sudo apt-get install nfs-kernel-server -y

設定ファイルを書き換えます。
sudo vim /etc/exports
以下の内容を追記(設定はひとつにつき一行に書いて折り返さない)。
/pxe/images/ubuntu-14.04-amd64  *(no_root_squash,ro,async,insecure)
/pxe/images/ubuntu-14.04-i386  *(no_root_squash,ro,async,insecure)


nfsサーバを再起動して設定を反映します
sudo service nfs-kernel-server restart

Ubuntu Disc Image

ftpでブートメニューが読まれた後に要求される、
起動したいUbuntu(Linux) のディスクイメージを、nfs上に展開します。
下記の空ディレクトリを作成します。
mkdir -p /pxe/images/ubuntu-14.04-amd64
mkdir -p /pxe/images/ubuntu-14.04-i386

ディレクトリにマウントします。
sudo mount -r -t iso9660 -o loop ~/ubuntu-14.04.1-desktop-i386.iso /pxe/images/ubuntu-14.04-i386/
sudo mount -r -t iso9660 -o loop ~/ubuntu-14.04-desktop-amd64.iso /pxe/images/ubuntu-14.04-amd64/

pxelinux.cfg/default (bootmenu)

クライアントPCがPXE機能で読み込む、ブートメニューや必要なバイナリを、ftp上に展開します。



ネットワークブート・メニュー表示に必要なバイナリを、以下から取得・配置します。
バイナリを取得
wget http://www.kernel.org/pub/linux/utils/boot/syslinux/syslinux-4.04.tar.gz
解凍し、中のファイルを以下のパスに配置します。
/pxe/menu.c32
/pxe/pxelinux.0

ネットワークブートのブートメニューを以下に設定ファイルとして配置します。
vim /pxe/pxelinux.cfg/default

設定ファイルの内容は以下 (IPアドレスは環境により適切に読み替えます。)
# /pxe/pxelinux.cfg/default
DEFAULT menu.c32
PROMPT 1
NOESCAPE 0
ALLOWOPTIONS 0
TIMEOUT 100

MENU TITLE PXE Boot Menu

LABEL Ubuntu 14.04 amd64
kernel images/ubuntu-14.04-amd64/casper/vmlinuz.efi
append boot=casper netboot=nfs nfsroot=192.168.1.21:/pxe/images/ubuntu-14.04-amd64 initrd=images/ubuntu-14.04-amd64/casper/initrd.lz -- splashd

LABEL Ubuntu 14.04 i386
kernel images/ubuntu-14.04-i386/casper/vmlinuz
append boot=casper netboot=nfs nfsroot=192.168.1.21:/pxe/images/ubuntu-14.04-i386 initrd=images/ubuntu-14.04-i386/casper/initrd.lz -- splashd

LABEL Ubuntu 14.04 amd64(non splash)
kernel images/ubuntu-14.04-amd64/casper/vmlinuz.efi
append boot=casper netboot=nfs nfsroot=192.168.1.21:/pxe/images/ubuntu-14.04-amd64 initrd=images/ubuntu-14.04-amd64/casper/initrd.lz


pxe-pdhcp

Sadayuki Furuhashi氏が作成したpxe-pdhcpをソースで取得し、ビルドして使用します。
今回はroot直下に/pxe-pdhcpディレクトリとしてソースとバイナリを展開しました。
sudo make  /pxe-pdhcp
chmod a+rw /pxe-pdhcp
cd /pxe-pdhcp
git clone https://github.com/frsyuki/pxe-pdhcp
なお、いくつかのブログでバッファオーバーフロー警告に対するコード修正が紹介されていますが、今回はしなくても動作したので、とりあえずコードを書き換えていません。

ビルドします。
make
実行します。(IPアドレスは環境により適切に読み替えます。)
sudo /pxe-pdhcp/pxe-pdhcp -l 0.0.0.0 -b 255.255.255.255 -t 192.168.1.21 -d pxelinux.0

クライアントPCからネットワークブートを実行

PXEサーバ(pxe-pdhcp)が起動している状態でクライアントPCからネットワークブート操作を行うことで、ネットワークブート可能です。



クライアントPCのネットワークブート方法は、マシンにより異なるので、サイト・マニュアル・グーグル検索を活用してください。
BIOS/EFIの設定画面で、PXEを有効にする必要があるかと思います。
とりあえず、一般的にはBIOS/EFI設定画面に行くなら、起動画面でDeleteキー押下かと思います。

また、ESC/F2キーなどでブートセレクタを呼び出せるPCもあります。
ブートセレクタにPXE(Network boot)が無い場合、BIOS/EFIの設定でPXEが無効になっているかもしれません。

ちなみに今回クライアントPCとして使用したN10Jcの場合は、
F2:BIOS設定
ESC:ブートメニュー
でした。

その他

手順中に、2箇所ほど、PXEサーバのIPアドレスを指定する個所があります。
(特にネットワークブートメニューのnfsroot設定)
そのため、PXEサーバマシンはIPアドレスを固定設定にすると、今後の利用が楽になります。

Ubuntuのディスク内容をNFS上に置く必要がありますが、毎度ISOファイルをマウントするのが面倒であれば、コピーして配置してしまうと楽になります。

Inkscapeのコードを読む 使用library一覧

現在、Inkscapeのコードを読もうと格闘中です。
今回は、ざっくりとInkscapeで使用しているライブラリの概要と一覧を作ったので、貼っておきます。



まず間違いなく使用ライブラリを網羅していないので、これがすべてではありませんが。
とりあえずInkscapeのソース中に直接取り込まれていて、目に付いたものを記載しています。
幾何学関連で実績のあるオープンソース・ライブラリの良い一覧くらいにはなるかと。

Inkscapeソースコードの取得

まずはInkscapeのコードを取得しましょう。
公式WikiのUsing a centralized (SVN-like) workflowを読みましょう。
http://wiki.inkscape.org/wiki/index.php/Working_with_Bazaar#SVN-style_checkout
ざっくり解説すると、Inkscapeはリポジトリにbazzer(bzr)を使っていて、ソースコードは以下のコマンドで取得できます。
bzr checkout lp:inkscape
bzr update



## libavoid

License:GPL
object-avoiding connector routing for use in interactive diagram editors.
http://www.adaptagrams.org/documentation/libavoid.html

## libcola

License:MIT
constraint graph layout(制約グラフ)
http://marvl.infotech.monash.edu/webcola/
を見るとjs版のサンプル画像が見れる。
https://github.com/mjwybrow/adaptagrams
によると、libavoidなどと同じチーム(?)の制作。

## libcroco

License:LGPL
Libcroco is a standalone css2 parsing and manipulation library.
https://github.com/GNOME/libcroco
http://lfsbookja.osdn.jp/BLFS/svn.ja/general/libcroco.html

## libdepixelize

License:不明
Inkscapeではピクセル画像をベクタ化する際に使っているとのこと。
http://research.microsoft.com/en-us/um/people/kopf/pixelart/
によると、どうやらMSのプロジェクトから来ているらしい。
ソース上のREADMEによると、リポジトリからコピーしたものだから更新する場合は本家リポジトリを更新した上でコピーせよ、とのこと。

## libgdl

License: LGPL?
dock layout widget.
GTK由来と思われるドッカー(ウィンドウ内で可変サイズの領域を取る内ウィンドウ的な)ウィジェット。

## libnrtype

License: (InkScape?)
typeface rendering(cross platform glue)
http://wiki.inkscape.org/wiki/index.php/Pangoification
によると、Sodipodi時代に書かれてPangoに置き換えられていくように読める。

## libuemf

License: GPL2
reading/writing Enhanced Metafile (EMF)
and Windows Metafile (WMF) files.
https://ja.wikipedia.org/wiki/Windows_Metafile
によると、EMFはWindowsのベクトル画像フォーマットとのこと。

## libvpsc

License: LGPL
? 何をやっているのか不明。
http://www.adaptagrams.org/documentation/libvpsc.html
によると、
solving for the Variable Placement with Separation Constraints problem.(分散制約問題)を解く、とある。


## libarot

License: (InkScape?)
rasterization and geometry library
http://libregraphicsworld.org/blog/entry/rock-around-0-48-interview-with-inkscape-team
によると、rasterizationはCairoに、geometoryは2geomに置き換えられていくように読める

## 2geom

License: LGPL
http://osdn.jp/projects/sfnet_lib2geom/
によると、幾何学計算のライブラリ。(抽象的な処理を受け持ち、レンダリング(ラスタライズ?)は担当しない?)


まとめ

adaptagrams.org という方(方々?)が作ったライブラリが多い。
・いくつかのライブラリは将来的に2geomにまとめられて置き換えられていくらしい。

libvorbisで.ogg音声ファイルをデコードする(memo)

vorbis形式の音声ファイルを再生するコードを書こうと思った際のメモです。
勿体なかったので公開。
書いたのは、vorbisをPCMデータに変換するまでです。
音を出していないので、正しさを確認していない&中途半端です。

PCM形式でメモリに置くまで試して、PCMデータのサウンド出力は実際に試さずに放置していました。

さらに、同じ内容で微妙に違う2記事を適当に統合したので、重複や間違いがあると思われます。


Gstreamer


libvorbisで、(ogg) vorbisファイルをデコードしてメモリ上にPCMデータを作るサンプル
Gstreamerで 、メモリ上のPCMデータを出力するサンプル
を組み合わせて、
Ogg VorbisファイルをPCMデータにしてからGStreamerに投げることを目指す

こんなことをしなくても、
GStreamerにファイルを渡せばOggVorbisのデコードまでしてくれるが。

わざわざこんなことをする理由は、
・Ogg Vorbisファイルがデコードできていることを確認したい。
・ネットワーク音声転送するにあたって、GStreamerにメモリ上の音声データを直接渡す方法を知っておきたい。

ネットワークを通すということは、ストリームでありバイナリデータ。
GStreamerにも音声伝送用のサーバ・クライアントはあるが、暗号化をかけるのが面倒そう&画像と音声以外を送るためにできていない。





PCM形式が使われるらしい。
(libvorbisもデコード時にPCM変換を行っている)

メモリ上にPCMデータを用意する
Gstreamerにメモリを読ませる。
Gstreamer内部形式に変換させる
出力まではGstreamr

なお、GStreamerの出力デバイス自動指定(autoplay)を使うので、これを指定して音が出ることが前提です。




(とても成功とは思えない) libvorbisサンプルプログラム実行成功の様子

libvorbis(libogg)とサンプルのコンパイル

ソースを入手するには、公式サイト http://www.xiph.org/downloads/ から圧縮ファイルかリポジトリを探します。
今回はGitリポジトリを使用しました。
公式サイトには、さらにサンプルコードとその解説が用意されています。

どなたかが日本語訳してくださったFAQによると、libvorbisを使用するには、liboggが必要とのこと。

ライブラリソースのダウンロードページからリポジトリ一覧を探し、その中からliboggとlibvorbisをcloneして取得しました。
ダウンロードページの圧縮ファイルを解凍して使っても構いません。

作業用のディレクトリを作り、その中でgitコマンドを実行しました。

git clone  git://git.xiph.org/mirrors/ogg.git
git clone git://git.xiph.org/mirrors/vorbis.git

libvorbisはliboggを必要とするので、oggからコンパイルします。
また、今回はインストール場所をプロジェクトディレクトリ下にするため、prefixオプションを使用しています。

cd ogg
./autogen.sh --prefix=$(dirname $(pwd))
make install
cd ..


続いてlibvorbisをコンパイルします。
(インストール先にすでにliboggのバイナリがあるためか、オプションでlibogg指定する必要はありません。)

cd vorbis
./autogen.sh --prefix=$(dirname $(pwd))
make install
cd ..

公式サイトにあるlibvorbisのドキュメント内にサンプルソースがあるので、コピーして使用します。
ファイル名をexample.cとして保存します。


makefileを書きます(必要箇所をtabで置き換えてください)。

BIN=vorbis.exe
SRC=example.c
ARGS=$(shell export PKG_CONFIG_PATH=lib/pkgconfig/:${PKG_CONFIG_PATH}; \
    pkg-config --libs --cflags vorbis vorbisfile)

$(BIN): makefile $(SRC)
    gcc $(SRC) -o $(BIN) $(ARGS)

clean:
    rm $(BIN)




ここまでの準備が済むと、作業ディレクトリはこのような構成になっているはずです。

 vorbis/        (libvorbisのソースディレクトリ)
 ogg/            (liboggのソースディレクトリ)
 include/
 lib/
 share/
 example.c            (プログラムソース)
 makefile


コンパイルして実行します。
デコードするoggファイルは、標準入力から与えます。
make
./vorbis.exe < "~/07 - Automatic analyzer.ogg"

シェル上に文字化けした謎の出力が出るようになったら成功です。
PCMデータを変換せずに標準出力に出力しているため、このような出力になります。

GStreamer-SDK導入とサンプルのコンパイル

以前の記事を参照してください。
チュートリアルをコンパイルして実行できるところまで確認しておくと良いです。


ここから先は、メモリ上のPCMデータを実際に再生する、そして解説がここに。
GStreamerは標準でoggファイルの再生機能を持っていますが、ここで重要なのは、メモリ上の生データ(PCM形式の配列データ)をGStreamerに流し込む方法と、それによるPCMデータの再生です。

spk121様のburro-engineより、
メモリ上のPCMビープ音をGstreamerで出力するサンプルを取得します。
さらに、GStreamerおよびこのコードについての解説が、GStreamer Tutorial #1: How I Made a Beep! にあります(英語)。

ビルド方法も、ソース付属のビルドスクリプトに準じます。



要するに、audio/x-raw-intを使って、PCMデータをGStreamerに流し込めば音が再生できるようです。




オープンソース・ソフトウェアプロジェクトの寄付先リスト(2013年版)

2013年あたりに作ったメモ。
捨てるのも勿体なかったので公開。
今では歴史的価値すら無いかもしれませんが。

もっと高額を寄付している人はいくらでもいますし。
あくまで少額寄付のススメとして。
もろもろの葛藤は「やらない善よりやる偽善」ということで。
とりあえず最大合計1万円を目安に、割り振っていきました。

選考基準

このソフトウェアに値段を付けるとこれくらい、という意味ではありません。日頃の利用率とも比例していません。

とはいえ。これが2015年現在であったなら、FontForgeに寄付しないではいなかったはずです。(とても使っているので)

注意

リンク先URLは参考程度に。いつURLが変わるとも限りませんし、リンク先が本物だと保証もできないからです。
「寄付のページがあるんだな」と確認するにとどめて、公式ページから寄付先を探してください。

2013/07/10にGoogleで見たら、1USD100.5円でした。
なので、1ドル100円換算とします。


Linux

500円
「カードorPayPal」で可能。
linuxfoundation.orgのDonateページ

まずはLinux。one time(一回きり)の方。割合としても少額ですが。
カーネルに重点を置いて寄付したいのですが、LinuxFoundationに入れておけばよいのかちょっと悩みます。
この寄付ページは検索で簡単に見つかります。

PayPalアカウントが使えなかったので、
Don't have a PayPal account?
Use your credit card or bank account (where available). Continue
の方へ。いろいろ入力しました。

GIMP

2000円くらい
PayPal、Bitcoinほかで可能。
Donateページ

2.8になって手ブレ補正がつき、イラストが描きやすくなりました。
Linuxデスクトップを使っていて画像編集したい場合に、オールインワンかつ必須のソフトウェアだと思います。

GNOME Foundationへの寄付になるようなので、コメントに(下手な英語で)「可能ならGIMP、またはGNOMEのソフトウェアプロジェクトへお願いします」と書いておきました。あくまで、他のプロジェクトへ回ってもOKという気持ちで。
間違って500円入れてしまったので、残り1500円をもう一度入金。
二度目は「Donate to gimp@gnome.org」って書いておきました。迷惑でなければよいのですが。



WINE

2000円くらい
「カードorPayPal」、Googleチェックアウト、メール で可能
Donateページ

WineHQから飛べます。
codeweaversからの資金援助もあるようです。とても良いことだと思います。
大学時代、WINEの仕組みを知った時には思わず胸が鳴りました。ソフトウェアがこんなにも面白いと知ったきっかけのひとつです。



ReactOS

2000円くらい
PayPal、Bitcoin、WebMoney ほかで可能。
Donateページ

WINEのコメントを書くまで忘れていたのですが、大学時代にドキドキさせてくれたプロジェクトです。プログラマを本気で志した理由のひとつでした。
ReactOSとOSASKには思い入れがあります。

ReactOSのクレジット決済ページだけなんで生年月日を訊かれるのかと思ったら、PayPal登録を兼ねたページなのだそうです。当然ながら「JPからは送金できない云々」。
支払方法のうち、WebMoney(ウェブマネー)は、日本のコンビニで売っている固有名詞のWebMoneyではありません。注意。間違って買ってしまいました。

MyPaint

2000円くらい寄付したかった。
寄付先がないため無理なようです。
「ありがとう。でも寄付はいらないよ」的なことがWeb上に書いてあったので良しとします。(そのページはセキュリティ証明書が切れていたので、本物かどうかちょっと怪しいですが)
 

Liferea

500円くらい
「カードorPayPal」で可能。
OpenHatchのDonateページ
Lifereaフィードリーダーを使っていたので、2013年のGoogle Reader終了騒ぎに巻き込まれずに済みました。
ところで寄付先は本当にここ(OpenHatch)でいいのでしょうか。
公式ページのリンクをたどると、上のリンクページにたどり着くのですが。


「Lifereaが好きです、ありがとう」(英語)とメッセージを書いておきました。
ここでPayPalから受領メールが来なくなる。不安。
カードの明細には該当する取引が記載されていたので、どうやらメールアドレスを記入ミスしたようです。

InkScape

1000円くらい
「カードorPayPal」、Googleウォレット で可能。
Donateページ
ベクタ系のイラストツール。まだまだ発展途上に感じるのは、私があのUIに慣れていないだけかもしれません。

こちらもPayPalからメールが来ませんが、やはりカードの明細には記入されていたので、メールアドレスの記入ミスだったようです。


Wikipedia

500円くらいクレジットカード、PayPalで可能。
寄付ページ
オープンソースプロジェクトとは違いますが。
いつもジミー・ウェールズ氏のバナーが表示されている印象があります。わたしがメインページを見たときは穏やかなものでしたが。

これまでのサイトとは違い、クレジット決済をPayPalのフォームを通さずにやれるようです(内部で使っていないかどうかはわかりませんが)
しかも、通貨単位がUSDではなく日本円です。
最初に2回クレジット決済に失敗したのですが、どうやらNoScriptがいけなかったようです。2つほど許可したら送金にクレジット決済に成功しました。

Little Witch Academia 2  ( by Studio TRIGGER )

500円くらい
Amazon経由(Kickstarterプラットフォーム)
プロジェクトのKickstarterページ
オープンソースプロジェクトでないものその2。既にまったく関係ない。
以前、はてブに上がっていたリンクで一話を見ました。
最近、はてブにこの話題が上がっていました。
偶然のタイミングでした。それだけです。

これを送金するためだけに、Kickstarterと米Amazonにアカウントを登録することになりました。




備考:今回は漏れましたがいつもお世話になっております高品質なソフトウェア様がたは、今度機会があれば、また検討します。

PayPalアカウントの作成方法

PayPalで日本から送金できない
PayPalで日本から送金できない


「PayPalでは、現在JPの買い手からの寄付支払いはサポートされません。」と出て使えません...。
 PayPal経由で寄付しようとしたのですが。

PayPalへアクセスして、アカウント作成のページで「パーソナル」を選択。
個人情報を入力
(パスワードのコピー&ペーストは禁止だそうです。不便。)
しばらくするとメールが来ますので、メール上からリンクをクリックして、秘密の質問を答えればアカウント登録は終了です。

寄付の手順
PayPalって書いてあるリンクをクリック
寄付先が対象プロジェクトなのか、GNOMEなどのグループなのか(グループなら希望を出せるのか(叶わなくてもOKというのが前提ですが))
PaylPalのダメな方だったらカード(VISAまたはMasterCardほか。JCBは使えない)のほうを選択。


PayPal カード決済
PayPalアカウントではなく、画像のリンクからクレジットカード決済 (WINE)


PayPal クレジットカード決済画面 GIMP
PayPal クレジットカード決済画面 (GIMP)


以上です。

RSSフィードをPHPで書かせた際のメモ

書きかけの汚いメモです。捨てるのも、もったいなかったので。

firefoxでダウンロードされる不具合。
.rdfではなく.xmlにするとダウンロードされない。

.rdfをxmlとしてApacheに設定すればよい(ダウンロードされない?)のだけれど、基本設定は変えられるとは限らないので、できるだけいじらない方針。(レンタルサーバへUpしたことがないので予防検疫的にコーディング。)

PHPでheader()を吐けば良いらしいけれど、RSSのURLが呼び出されるたびにPHPでRSSを生成させたり、生成済みファイルにheader()を付与するためだけのPHPファイルを追加したくなかった。

RSS自体の書き方。
RSS2.0で画像・リンク有り概要を実現する「<content:encoded>」を使う
そのまま使うとRSSリーダ(Liferea)がエラー。リンクからRSSを開くとFirefoxもエラーを吐く。
検索で上手く見つからず、考えた結果、自己解決。
RSS拡張するだけではダメ。(<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">の追記だけではダメということ。)
結論:CDATAセクションで囲む。
<description>もこれで囲んであった。

以上。

ルーン文字フリーフォント集 手書き風の新フォントを追加


ルーン文字フリーフォント集のRuneAMN_SeriesFonts に、新書体として KnifeAフォント および KnifeArフォント を追加しました。
楔文字(くさび文字)を起点にして、手書き文字をイメージした字形になっています。



RuneAMN_Series_Fontsは、デザイン・イラスト向けのルーン文字フォント集です。
複数のルーン文字フォントを収録した、daisy bellのフリーなフォント製品です。

RuneAMN_Freeシリーズフォント


RuneAMN_KnifeA(r)のねらい

RuneAMN_Serif

これまでRuneAMN_Freeに収録されていたフォントは、正確な直線を基本として成る、ベーシックなセリフ・サンセリフ書体としてデザインされました。
その字形は、端正で美しいものです。反面、産業革命以降の印刷物に使われた金属活字・写植技術の流れを汲む、どこか人工的な文字の形でもあります。
ルーン文字が役立つ場面の多くは、魔法をコアとする、古代〜中世ヨーロッパ的なイラストです。

もちろん、セリフ・サンセリフ書体が魔法世界と反発するということはありません。むしろセリフ書体は時計の時刻を示すローマ数字を連想させて、魔法と非常に相性が良い字形です。実際に私が100個のルーン魔法陣をデザインした際には、サンセリフ書体と共に大活躍しました。
一方で、産業革命以前の、手書きあるいはナイフで木版や石版に描いたようなルーン文字フォントを作れば、RuneAMNユーザが描けるデザインの幅が広がるのではないかと考えました。

そこでデザインされたのが、RuneAMN_KnifeA~Dフォントです。
2015/07現在、全7種類が存在します。4個より多いのは、"r"(Reverse/反転)と"Bold"(太字)を含むからです。
これらを総称して、 RuneAMN_Kinfe's あるいは、
プログラマの慣習に従って RuneAMN_Knife*フォント と呼称します。

今回は、この中からRuneAMN_KnifeAおよびKnifeArを、フリーフォントとしてリリースしました。
KnifeA(r)があれば、ルーン文字による自然物への手書きデザインが簡単になります。

## RuneAMN Free Series Fonts アップデートについて
今回のRuneAMN Free Series Fontsのアップデートには、上記のKnifeA(r)フォント2つを、新フォントとして追加しました。


また、試験的に10点添付していたルーン魔法陣素材も、30点に増やしました。
魔法陣素材は、AI/SVGによる、透過ベクター形式です。
透過素材なので、簡単に貼り付けて使えます。ベクター画像なのでどれだけ拡大してもボケないので、ピクセル数の大きい画像や印刷物でも、大きなサイズで使うことができます。


(英語版記事 / For english of this article)

ダウンロード

RuneAMN_Freeのダウンロードは、
daisy bellのPixiv/BOOTH
または、
daisy bellの配布ページ[OSDN]
からどうぞ。

フォント開発に使っているFontForgeスクリプト他をオープンソースで公開しています。
RuneAMN_Pro_Series_Fonts[github]

RuneAMN_Proについて


RuneAMN_Proは、RuneAMN_Freeには無い、意図して特定用途に絞り、個性的なコンセプトで作成されたフォントたちを収録したフォント製品です。
RuneAMN_Proに、KnifeフォントのB~D(一部さらに"r"および"Blod")までが収録されています。手書き風に崩したKnifeDと、これをさらに太字化したKnifeDBlodが含まれます。
KnifeB以降のフォントは、すべてKnifeAの派生デザインです。そのため、実のところ基本のデザインにはKnifeA(r)だけあれば十分です。
KnifeB以降は、デザインのディティールを、より繊細にコントロールしたいユーザ向けのオプションとして用意しました。

なお、RuneAMN_Proでは、RuneAMN_Freeに収録済みの魔法陣に追加をおこない、合計100点の魔法陣素材を添付しています。


今後の予定

RuneAMN_Freeに、さらなる新フォントを増やす予定です。(約束することはできませんが。)
画像が、新フォント KnifeE の候補です。
(こちらのほうがより楔文字らしい?)



というわけで、RuneAMN_FreeシリーズフォントおよびKnifeA(r)フォントをよろしくお願いします。

ブラックレター書体のデザイン・チートシート


project daisy bellのRuneAMN_Proシリーズフォントには、BlackletterおよびBlackletterPlowとして、ルーン文字のブラックレター書体をデザインして収録しています。
 (BlackletterPlowは次回リリースで収録予定。)

このルーン文字版ブラックレター体フォントをデザインするにあたり、既存のラテン・アルファベットフォントから、デザイン性の高い複数のブラックレター体を参考にして、ブラックレター書体の特徴を抽出する作業を行いました。
この記事のチートシートは、その際に観察結果を自分用にまとめたものです。

RuneAMN_Proのブラックレター体 書体見本

装飾性の高いブラックレター体の特徴を知り、使いこなすことで、作品タイトルやロゴに最適化された専用のブラックレター文字をデザインすることができます。
また、既存フォントのデザインをロゴ用に調整する際も、不自然になるのを避けることができるようになります。

いちおう注意点を。このチートシートはあくまで私製で、観察の結果をまとめたものです。
ブラックレター体のデザインについて、体系立った公式の解説が欲しい方は、カリグラフィーの教科書などに正確な情報が載っていると思われるので、それらをご参照ください。
(というか、そういったものがWeb上に見つからなかったので仕方なく作ったものです。もっと詳しい方からマサカリが飛んでくるのを待っています。)
















以上です。
slideshareにUpすればよかったのかもしれませんが、2000x1000で作ってしまったものは仕方ない。

上の項目をまとめたチートシートを下に置いておきます。


Pixiv(ユーザページ)にも同じものをUpしておきます。
本ページの内容が更新された際はTwitterアカウントで告知するかもしれません。


同人誌・ イラスト・デザイン向けダウンロード販売サイトの比較一覧


ダウンロード販売しているフォント製品の、購入方法の選択肢を増やそうと思い立ち、ダウンロード販売サービスを調査しました。
その防備録です。

拠点としてPixiv/BOOTHにdaisy bell 製品紹介ページを作っており、BOOTHアカウントを持っていないユーザのために、Gumroadでも同じフォント製品を取り扱っています。

https://daisy-bell.booth.pm/


"project daisy bell"はオリジナルのデザインフォントを販売しています。
これまでの販売履歴をチェックしてみたところ、代引き購入が多いことがわかりました。
これは、クレジットカードが多いだろうという私の予想に反していました。
また、Pixiv/BOOTHのアカウントを持ってない(アカウント作成もしたくない)ユーザのために用意したGumroadが、あまり有効ではないことがわかりました。
そこで、
・そのダウンロード販売サイトのアカウントを持っていなくても購入できる
・代引きによる支払いで購入できる
という条件で、ダウンロード販売サイトを検討することにしました。

フォント製品の主な想定ユーザ様は、個人のデザイナ・イラストレーターの方々です。
そのため、調査の対象は、イラストサイトや同人誌ダウンロード販売に関連したサイトが中心となっています。


現在、"project daisy bell"による有償フォント製品は、以下の2つです。


RuneAMN_Pro_Series_Fonts  1600円
RabbitMaluka  600円
(価格は2015/05時点でのものです)

RuneAMN_Pro_Series_Fontsは16種類のルーン文字書体を収録したデザインフォント集で、フォントとしてはともかく、同人関連のDLサイトで扱う製品としては、額がひとつ大きな区分に入ります。
 RabbitMalukaは、検討したサイトで扱っている同人誌などの商品と、同じくらいの値段になっています。

ダウンロード販売サイト一覧

チェックしなかった項目

・税別とか面倒なので無視した・購入側が代引き・口座振込を選んだ場合の手数料は無視した
・販売側へ売上を振込む際の手数料も無視した
・購入ページのSSL対応とかチェックしていません
・(特に同人誌をPDFなどで販売する場合)専用ブラウザの出来とかDRM付けたいとか付帯サービス(例:スキャン代行)などがあるかと思いますが省きました
・購入時支払い方法の多さもチェックしていません
  (代引き対応していないところは一覧に含まれて無いだろうと思う)
・初期費用・維持費をチェックしていない
  (かかるところは一覧に含まれて無いだろうと思う)

チェックした項目の概要

手数料:
 ・ダウンロード販売の手数料のみで、必須でなければ代引き手数料などは含めていません
 ・もちろん安いに越したことはありません。
購入時の会員登録有無:
 ・今回は、『購入の間口を広げる』という目的があるため、会員登録が必要なサービスは(私的な)比較評価点を下げました
引き落とし:
 ・「自動振込」であると好ましい(販売サイトの管理なんてしたくないので)
 ・保留繰越となる下限額が低いほど良い(LINEスタンプの下限1万円は、私には高かった)

チェックし忘れた項目

・売れ行きチェックのしやすさ
  (管理ページの使いやすさ)
・購入者への無償アップグレードサポートのしやすさ
  (購入者のメールアドレス程度がわかっていないと購入の確認が面倒になる)

一覧の最後に、参考URLと雑感を書いておきました。
お約束として:この一覧は、私がざっと読んだ範囲の要約です。また、2015/05時点のものです。正確な情報は各サイト様の公式な案内を参照してください。

一覧

#### Pixiv/BOOTH

手数料: 3.6%+10円 (販売価格から引く)
購入時の会員登録有無: 必要
引き落とし: 1000円まで保留繰越 (5000円で自動振込)
参考URL:
https://booth.pm/faq

販売者の商品一覧ページが作れる(作らなければならない)。
とりあえずここをメインで使っている。Analytics連携がある。
0円にするとダウンロード数の時間軸統計がもらえない(有料コンテンツの場合は売上一覧がある)。

#### Gumroad

手数料: $0.25+5% (販売価格から引く)
購入時の会員登録有無:
引き落とし: 保留繰越なし。自動振込。
参考URL:
https://help.gumroad.com/customer/portal/articles/1569605-what-are-gumroad-s-fees-
https://gumroad.com/features/pricing

販売側は、売上をPayPalで受け取ることもできる。
購入者側の支払い方法はカードとPayPal。代引き・振込による支払いはできない。
Analytics連携がある。

#### DLMarket

手数料: ~1,000円 14.3% / 1,001円~     9.6%+57円 (販売価格から引く)
購入時の会員登録有無: 必要(twitterなどへのアカウント連携でも購入可能)
引き落とし: 316円~50000円まで選択可能(初期設定は"3000円")。自動振込。
参考URL:
http://www.dlmarket.jp/consignors/
http://www.dlmarket.jp/user_data/commission.php
http://www.dlmarket.jp/user_data/kessai.php


#### DMM

こちらに大体書いてあります。
http://www.dmm.co.jp/dc/doujin/recruitment/index_html/=/ch_navi=circle_recruit/
購入者の決済方法が「クレジットカードのみ」と書いてある項目があり、範囲外なのかもしれませんが、調べるのをやめました。
http://www.dmm.co.jp/mono/top/guide/store_html/=/ch_navi=/#about01-ms


#### メロンブックス

手数料: (一律30% 1000円以上で振込手数料が微変化)
購入時の会員登録有無: 不要
引き落とし(額etc): 保留なし (1000円まで手数料なし) > 公式サイトに記述がなかったのでメールにて問い合わせ
参考URL:
http://www.melonbooks.com/index.php?main_page=static&page=help/entry
http://www.melonbooks.com/index.php?main_page=static&page=help/works#rgst_works
http://www.melonbooks.com/index.php?main_page=static&page=help/works#sales_site

#### とらのあな

手数料: 300/648 1000/1620 1450/2160
購入時の会員登録有無: 必要
引き落とし(額etc): 3000円まで保留繰越
参考URL:
http://www.toranoana.jp/mailorder/guide/map/index.html
https://www.toranoana.jp/dojin/dl.html

#### DLSite

手数料: 300/600 1100/1600 1400/1900
購入時の会員登録有無: 不要
引き落とし(額etc): 3000円まで保留繰越
参考URL:
http://www.dlsite.com/home/circle/invite#gi4
http://www.dlsite.com/home/welcome#


おわりに

で、どのサービスを使うかですが...これから検討します。
販売サイトは多ければいいというわけではなく、管理が大変ですし、ユーザにとっても選ぶのが苦痛になりますので、あまり増やさないように気をつけるつもりです。

一方で、この一覧はまだ十分とは言えないと思うので、これを元に誰かがより良い一覧を作ってくださると嬉しいです。

CodeIgniter3で、GETパラメタによるページネーション

CodeIgniter3で、GETパラメタによるページネーション

paginateの出力(デフォルト設定1/2ページ)

ドキュメントは以下。
http://www.codeigniter.com/userguide3/libraries/pagination.html

でも、結局はCodeIgniterのソース(system/librarys/pagination.php)を読みました。
最初からソースを読んだほうが早かったと思われます。
今回はCodeIgniterが持つレイヤの薄さに助けられました。

要求としては、ごくありきたりなGETを使ったページ指定により、
" http://{PAGE_URL}?page=2 "
のようなURLでページネートを実現することです。

基本の方法

最終的に以下のように設定しました。

application/controllers/任意のコントローラ.php
$count_image_files = $this->Image_file_model->get_count_image_files();

$per_page = 10;
$config = array();
$config['base_url'] = current_url();
$config['total_rows'] = $count_image_files;
$config['per_page'] = $per_page;
$config['use_page_numbers'] = true;
$config['page_query_string'] = true;
$config['query_string_segment'] = "page";
$config['full_tag_open'] = '<span class="pagination">';
$config['full_tag_close'] = '</span>';
$this->pagination->initialize($config);

$params = array();
$params['page'] = $this->input->get('page');
$params['par_page'] = $per_page;
$image_files = $this->Image_file_model->get_image_files($params);

・base_urlはCodeIgniterに取得させます。
・total_rowsが全コンテンツ数で、par_pageが1ページごとのコンテンツ数です。
・use_page_numbersとpage_query_stringが、GETでページネーションする際のキモのようです。
・query_string_segmentにてGETキー文字列を変更します。
  なお、デフォルトのキーはなぜか'per_page'だったりします。
・full_tag_* はドキュメント参照。あまり関係ありません。
・ ページ番号はpagination自身が自分の設定から判断してGETなどから取得するようです。

モデル Image_file_model のメソッドは、それぞれ総数(int)と詳細(array配列)を返すものです。このあたりは解説しなくてもpagination機能との関連を読めば想像がつくと思います。

デフォルト設定ファイルの使用

CodeIgniterアプリケーション(Webサイト)内で共通で使うpagination設定を、設定ファイルに切り出すことができます。
以下のコードの動作を確認しました。

application/config/pagination.php
$per_page = 10;
$config = array();
// $config['base_url'] = current_url();
// $config['total_rows'] = $count_image_files;
$config['per_page'] = $per_page;
$config['use_page_numbers'] = true;
$config['page_query_string'] = true;
$config['query_string_segment'] = "page";
$config['full_tag_open'] = '<span class="pagination">';
$config['full_tag_close'] = '</span>';


デフォルト値を設定したことにより、上で書いたコントローラのコードは、以下のようになりました。
application/controllers/任意のコントローラ.php
$config = array();
$config['base_url'] = current_url();
$config['total_rows'] = $count_image_files;
$this->pagination->initialize($config);

$params = array();
$params['page'] = $this->input->get('page');
$params['page'] = (empty($params['page']) ? 1 : $params['page']);
$params['par_page'] = $this->pagination->per_page;
$image_files = $this->Image_file_model->get_image_files($params);


以上、参考になれば幸いです。

FontForgeでフォントに「文脈依存の置換(conditional features)」を設定する


FontForgeでフォントに「文脈依存の置換(conditional features)」を設定する手順を解説します。
文脈依存の置換は、前の文字によって次の文字を異字体に置き換える機能です。
筆記体(Script)書体にはぜひ必要な機能です。文字間の線を滑らかに繋げるために、前の文字に合わせて字形を変えなければならないからです。
Conditional Feature in my font.
caltにより設定された条件により、aaltによる置換を呼び出すものと思われます。 わたしも本設定を試している途中なので詳細はまだ不明です。
 
なお、本手順中でLookupとSubtableの名称は、FontForgeが自動生成するデフォルトのものを使用しました。
FontForgeは、日本語環境では日本語の設定名を生成します。
今回は、LANG=Cを頭に付けたコマンドライン呼び出しにより、英語ロケールとUIで作業を行いました。
英語のリファレンスを検索しやすい、などの利点もあるので、英語ロケールで作業することをおすすめします。


References:
チュートリアル (その 7)』による条件付き機能による置換を使用します。英語版『Tutorial #7 (Conditional Features )』も、日本語版が少し古くなっているので、必要に応じて参考にしました。
しかし、この英語版も説明が丁寧ではないので(あるいは英語版も古くなっているので) 、既存の条件付き置換を使用したフォントをFontForgeで開いて設定を読んだりしました。

This sample setting:
letter "b" を前の文字によって "b.high" に置き換えます。
"b.high"には適当なコードポイントを割り当てました。これがテストであるためです。本当はフォント固有の異字体を置くためのコードポイントが定義されているので、その領域を使うべきです。

## Create "b.high" letter




適当なコードポイントに"b.high"を作成します。
デフォルト設定では、一部の文字を覗いてユニコードコードポイントによる"uniXXXX"といった名前が付いています。エディタ上部の名前入力欄か、MenuのElement>GlyphInfo(グリフ情報)から、グリフの名称を"b.high"に変更します。

Setting lookup aalt(Access All Alternates)

Lookup aalt

Menu "Element" > "Font Info"
エレメント > フォント情報

"Lookup"/"GSUB" tab > "Add Lookup" button
 Lookups タブの "add lookup"

Type: "Single Substitution" > Feature: "Access All Alternates" (aalt)
種類:単純置換 > 機能:すべての異字体へのアクセス

どれがaaltなのかわからない場合は、種類:単純置換 > 機能:必須の機能 で出てくる RQDを指定してから、直接"aalt"とタイプして書き換えることができます。

Select GSUB Lookup features.

# Subtable aalt

Selected target aalt lookup.
"Add Subtable" button
Base Glyph Name: "b" / Replacement Glyph Name: "b.high"



aalt setting.

## Setting lookup calt(Contextual Alternates)

# Lookup calt

"Lookup"/"GSUB" tab > "Add Lookup" button
Type: "Contextual Chaining Substitution" > Feature: "Contextual Alternates" (calt)
種類:文脈連鎖依存の置換 > 機能:文脈依存の異字体

GSUB add lookup.

注意:この時、Type: "Contextual Substitution"を選んでもcaltを作成することができますが、
こちらは同じcaltでも、目的である文脈連鎖による異字体の設定ができません。
必ず、Type: "Contextual Chaining Substitution" を選択してください。



# Subtable calt

Edit Cahining Substitution "By Classes" / Dialog Type:"simple"
"クラスで指定" / Dialog Type:"シンプル"

以下は日本語環境でも同じ
Match Classes(1) Name:"hight-after" Glyphs in the class:"v"
Match Classes(2) Name:"letters" Glyphs in the class:"a b c d e f g"
Matching rules based on a list of classes:
"high-after | letters @<'aalt' Access All Alternates in Latin lookup 0> |"


select match type and dialog type.


edit calt features.

# Check setting conditional features.

Menu: Window > New Metric Window
Menu:ウィンドウ > メトリックウィンドウを開く
write: "abvb"

Conditional Feature (Metric Window).

この記事は「FontForge conditional features (calt/aalt)」の日本語版です。


by MichinariNukazawa(ja) @ Project daisy bell(product freefont/font)

イラストを艦これとアズールレーンに分類する

アジェンダ nijiflowをベースに、機械学習で艦これとアズールレーンの画像の分類器を作り、学習済みグラフ等を公開した。 また、これのTwitterBotを作成し公開している。 データセットはPixivのタグ付け済み画像情報を元に作成した。 艦これ・アズールレーンそれ...