にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

LaTeX (TikZ)でキーボード配列表を作成

XeLaTeXでTikZを使ってキーボード配列表を作っていた。101キーボード向け。
多言語用のキーボード配列を作るときに便利…なはず。とても泥臭いので誰か改良してください。

ちなみにXeTeXを使ったのはモンゴル文字への対応がLuaLaTeXは良くないためで、試してないがfontspecが使えればLuaLaTeXなどでも普通に使えるかもしれない。

ソース

\documentclass{standalone}
\usepackage{fontspec}
\usepackage{lmodern}
\setmainfont{Times New Roman}

\usepackage{readarray}

\usepackage{calc}
\usepackage{xparse}
\usepackage{listings}
\usepackage{tikz}
\usetikzlibrary{positioning,shapes,fit}

\tikzstyle{abstract}=[rectangle, draw=black, rounded corners, fill=white,text centered, text=black, text width=8mm]
\tikzstyle{gkey}=[rectangle, draw=black, rounded corners, fill=gray!20,text centered, text=black, text width=8mm]

\newcommand{\mykey}[2]{%
\begin{tikzpicture} \node (Item) [abstract, rectangle split, rectangle split, rectangle split parts=2]%
{#1 \nodepart{second}#2};%
\end{tikzpicture}}


\tikzset{
    pics/vhsplit/.style n args = {4}{
        code = {
        \node[anchor=west] (A) at (-3mm,-0.5mm) {#1};
        \node[anchor=west] (B) at (-3mm,-6.5mm) {#2};
        \node[anchor=east] (C) at (9mm,-0.5mm) {#3};
        \node[anchor=east] (D) at (9mm,-6.5mm) {#4};
        \draw[rounded corners=1mm] (-3mm,-9.5mm) rectangle (9mm,2.5mm); 
        }
    }
}
\tikzset{
    pics/specialkey/.style n args = {2}{
        code = {
        \draw[rounded corners=1mm,fill=gray!20] (-3mm,-9.5mm) rectangle (#2-1mm,2.5mm);  
        \node[inner sep=1mm,anchor=south west,text width=#2,align=center] (A) at (-3mm,-9.5mm) {#1};  
        }
    }
}


\makeatletter

\newlength{\my@keylength}
\setlength{\my@keylength}{13mm}

% {name}{label}{xpos}{ypos}{width}
\def\my@specialkey#1#2#3#4#5{%
  \path pic (#1) at (#3\my@keylength,#4\my@keylength) {specialkey={#2}{#5\my@keylength}};
}
% {name}{index}{north-west}{south-west}{north-east}{south-east}{vertical-index}{left-offset}
\def\my@keypath#1#2#3#4#5#6#7#8{%
  \path pic (#1) at ([xshift=#8\my@keylength]#2\my@keylength,#7\my@keylength) {vhsplit={#3}{#4}{#5}{#6}};
  \typeout{#1/#2/}
}
% \keyassign{1st}{2nd}{3rd}{4th line}
\def\keyassign#1#2#3#4{%
  \begin{tikzpicture}%
    \newcount\my@keycount %
    % 1st line
    \my@keycount=0
    \@for\lp@elem:=#1\do{%
      \expandafter\def\expandafter\my@cnum\expandafter{\the\my@keycount} %
      \expandafter\def\expandafter\my@keyname\expandafter{\expandafter{\expandafter a\my@cnum}} %
      \expandafter\def\expandafter\my@keyindex\expandafter{\expandafter{\my@cnum}} %
      \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\my@keypath\expandafter\expandafter\expandafter\my@keyname\expandafter\my@keyindex\lp@elem{0}{0} %
      \advance\my@keycount by 1 %
    } %
    \my@specialkey{BS}{Back\\Space}{13}{0}{1.27}
    % 2nd line
    \my@specialkey{Tab}{Tab}{0}{-1}{1.27}
    \my@keycount=0
    \@for\lp@elem:=#2\do{%
      \expandafter\def\expandafter\my@cnum\expandafter{\the\my@keycount} %
      \expandafter\def\expandafter\my@keyname\expandafter{\expandafter{\expandafter b\my@cnum}} %
      \expandafter\def\expandafter\my@keyindex\expandafter{\expandafter{\my@cnum}} %
      \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\my@keypath\expandafter\expandafter\expandafter\my@keyname\expandafter\my@keyindex\lp@elem{-1}{1.5} %
      \advance\my@keycount by 1 %
    } %
    % 3rd line
    \my@specialkey{CapsLock}{Caps Lock}{0}{-2}{1.57}
    \my@keycount=0
    \@for\lp@elem:=#3\do{%
      \expandafter\def\expandafter\my@cnum\expandafter{\the\my@keycount} %
      \expandafter\def\expandafter\my@keyname\expandafter{\expandafter{\expandafter c\my@cnum}} %
      \expandafter\def\expandafter\my@keyindex\expandafter{\expandafter{\my@cnum}} %
      \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\my@keypath\expandafter\expandafter\expandafter\my@keyname\expandafter\my@keyindex\lp@elem{-2}{1.8} %
      \advance\my@keycount by 1 %
    } %
    \my@specialkey{Enter}{Enter}{12.80}{-2}{1.47}
    % 4th line
    \my@specialkey{Shift}{Shift}{0}{-3}{2.07}
    \my@keycount=0
    \@for\lp@elem:=#4\do{%
      \expandafter\def\expandafter\my@cnum\expandafter{\the\my@keycount} %
      \expandafter\def\expandafter\my@keyname\expandafter{\expandafter{\expandafter d\my@cnum}} %
      \expandafter\def\expandafter\my@keyindex\expandafter{\expandafter{\my@cnum}} %
      \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\my@keypath\expandafter\expandafter\expandafter\my@keyname\expandafter\my@keyindex\lp@elem{-3}{2.3} %
      \advance\my@keycount by 1 %
    } %
    \my@specialkey{Shift}{Shift}{12.3}{-3}{1.97}
  \end{tikzpicture}%
}
\makeatother


\begin{document}

\keyassign{{}{}{\char"07E}{\char"060},{}{}!1,{}{}@2,{}{}\#3,{}{}\$4,{}{}\%5,{}{}{\char"05E}6,{}{}\&7,{}{}*8,{}{}(9,{}{})0,{}{}\_-,{}{}+=}%
{{}{}{}Q,{}{}{}W,{}{}{}E,{}{}{}R,{}{}{}T,{}{}{}Y,{}{}{}U,{}{}{}I,{}{}{}O,{}{}{}P,{}{}\{[,{}{}\}],{}{}|{\char"05C}}%
{{}{}{}A,{}{}{}S,{}{}{}D,{}{}{}F,{}{}{}G,{}{}{}H,{}{}{}J,{}{}{}K,{}{}{}L,{}{}{:}{;},{}{}{\char"022}{\char"027}}%
{{}{}{}Z,{}{}{}X,{}{}{}C,{}{}{}V,{}{}{}B,{}{}{}N,{}{}{}M,{}{}{<}{\char"02C},{}{}{>}.,{}{}?/}


\end{document}

これをXeLaTeXでコンパイルすると次の様に描画される。
f:id:nixeneko:20170106192541p:plain

雑な解説

簡単に解説しておくと、\keyassignマクロは4つの引数、すなわち順に(上から)第1行目(キー13個)、第2行目(キー13個)、第3行目(キー11個)、第4行目(キー10個)のキー配列を引数としてとり、キー配列を描画する。
キー配列はキーをカンマ“,”で区切ったものである。
キーは文字4つ(何も文字がないところには{}を置く)からなり、左上、左下、右上、右下の順である。複数文字を一つの場所に入れる場合には{}で囲む。なお(La)TeXの特殊記号となっているものはエスケープしたりしないといけない。


\begin{document}~\end{document}の中の\keyassignマクロの部分を変えると他のキーボード配列を作成することができる。例えばロシア語キーボード配列だと、

\keyassign{{\char"07E}{\`{}}{}Ё,!1{}{},@2{\char"022}{},\#3№{},\$4;{},\%5{}{},{\char"05E}6:{},\&7?{},*8{}{},(9{}{},)0{}{},\_-{}{},+={}{}}%
{Q{}{}Й,W{}{}Ц,E{}{}У,R{}{}К,T{}{}Е,Y{}{}Н,U{}{}Г,I{}{}Ш,O{}{}Щ,P{}{}З,\{[{}Х,\}]{}Ъ,|{\char"05C}/{}}%
{A{}{}Ф,S{}{}Ы,D{}{}В,F{}{}А,G{}{}П,H{}{}Р,J{}{}О,K{}{}Л,L{}{}Д,{:}{;}{}Ж,{\char"022}{\char"027}{}Э}%
{Z{}{}Я,X{}{}Ч,C{}{}С,V{}{}М,B{}{}И,N{}{}Т,M{}{}Ь,{<}{\char"02C}{}Б,{>}.{}Ю,?/.{\char"02C}}

のようにして、次のような画像が得られる。
f:id:nixeneko:20170106202632p:plain



なぜこんなものを作ったかというと、モンゴル文字キーボードのキー配列表を作りたかったからで、フォントの変更や文字の回転などを組み合わせて次のようになった。なかなかいい感じだと思う。
f:id:nixeneko:20170106200748p:plain


というか、作ってから時間たってるので、マクロ内部で何やってるのか覚えていない。何やら\expandafterとTikZに苦しんでいたのは覚えているが……。

無理な時に押すボタンをつくった

最近いろいろと無理なので無理な時に押すためのボタンをつくった。Arduino Unoを使って押すとむーりぃーって言うもの。

へぇボタンじゃねーか。

ハードウェア

Arduino Uno (R3)を使った。

全体図こんな感じ。
f:id:nixeneko:20161223184726p:plain

電源

Arduinoへの電源供給様に電池6Vを3端子レギュレータで5Vに降圧してArduinoのUSB端子につなげている。
普通にUSBで電源につなぐので問題ない。

ボタン

ボタンは100円ショップで売っている押すとオン・オフが切り替わるプッシュライトのスイッチを改造した。

上のサイトを参考に、スイッチ内部でカチカチと引っかかる部品を取り除き、押している間だけスイッチが切り替わるようにする。また、押している間通電するようにしたかったので元の配線から配線を付け替えている。
さらに、ライト部分も生かしたかったのでそこにもリード線をつけて外に引っ張りだした。

ライトを開けるとこんな感じ。
f:id:nixeneko:20161223185855p:plain

配線

こんな感じの回路図になると思う。入力ピンに繋がれる部分は、ボタンが押されてないときはGNDに、押されてる間は+5Vになる。
f:id:nixeneko:20161223182431p:plain

実装時には、あまり良くないけどプルダウン抵抗は省略した(要するにプッシュライトのLED用抵抗がプルダウン抵抗の役目を果たすことになる)が、一応問題なく動いているようである。
f:id:nixeneko:20161223191024p:plain

ソフトウェア

音声の出力については

を参考にした。

音声を用意する

今回はArduinoフラッシュメモリ(32kB)に書きこむので、データのサイズをメモリに乗る程度に小さくしないといけない。という訳でメモリぎりぎりのサイズになるようサンプリング周波数を調整した。

  • サンプリング周波数13800Hz
  • モノラル
  • 符号なし8bit
  • ヘッダなし

の音声ファイルをAudacityを使って用意した。音量は割れない程度に大きめにしておいた。
f:id:nixeneko:20161220003235p:plain
ファイル名はmuri13k8.rawとした。


次に、Arduinoで読み込めるように、.hファイルに8ビット符号なし整数の配列として書き出す。
適当なpythonスクリプトconv.pyを書いて変換する。

with open('muri13k8.raw', 'rb') as f:
    data = f.read()
print('const unsigned char muri[] PROGMEM = {')
for x in data[:-1]:
    print('{},'.format(x), end="")
print('{}}};'.format(data[-1]))

この出力をmuri.hとして保存し、スケッチのフォルダに突っ込んでおく。

python conv.py > muri.h

muri.hの内容はこんな感じになる。

const unsigned char muri[] PROGMEM = {
126,125,125,125,125,125,125,125,125,125,125,125,(以下略)
};

Arduinoコード

Arduinoのプログラムを書く。Arduino IDEのバージョンは1.6.11。

#include <avr/pgmspace.h>
#include "muri.h"
#define ARRAYSIZE(x)  (sizeof(x) / sizeof((x)[0]))

#define OUT_PIN 3
#define BUTTON 19

int i = 0;       // 音声の再生位置
int lastval = 0; // ボタン状態レジスタ

void setup()
{
  pinMode(BUTTON, INPUT);
  pinMode(OUT_PIN, OUTPUT); 

  // http://qiita.com/kinu/items/6cd5da0415e31834e7da から
  //   これを設定するとすごい速さで動くようになるらしい
  // Non-inverting fast PWM mode on Pin 3.
  // COM2B1:0 ==  10: Non-inverting mode
  // WGM22:0  == 011: Fast PWM mode, 256 cycle (16MHz / 256 == 62.5kHz)
  // CS2:0    == 001: No prescaler (runs at maximum rate, 62.5kHz)
  TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(CS20);

  i = ARRAYSIZE(muri); // 再生位置を最後に指定し自動再生されなくする
}

void loop()
{
    if(i < ARRAYSIZE(muri)){
      OCR2B = pgm_read_byte_near(&muri[i]); // 音声データ書き込み
      i++;
    }
    lastval = (lastval << 1) | digitalRead(BUTTON); // チャタリング回避
    if(lastval == 0x7FFF)            // ボタンが押されたら
      i=0;                           // 再生位置を0に

    delayMicroseconds(72);   // 1000000microsec / 13800Hz
}
  • setup()関数内でマイコンが早い周波数で動くように設定している。
  • 単純にloop()関数内で音声の周期(72μs)ごとに音データを書き込んでるだけで、ボタンが押されると音声データの配列のインデックスを0に更新することで最初から再生されるようにし、ボタンを連打できるようにしている。
  • チャタリング回避を行っている。具体的には、digitalReadの値がボタンが押されていないときに0、押されているときに1となるのを利用して、0のあと15回1が続いた場合、信号が安定したとみなして音の再生を行う。ボタンの数値のログを保管するのにはシフトレジスタを使い、毎回レジスタを1ビット左シフトしてLSBにdigitalReadの値を入れていく。シフトレジスタ(lastval)の値が0b0111 1111 1111 1111となったときにボタンが押されたときの操作を実行する(再生位置を0に更新する)。これによりチャタリングが抑制されボタンを離した時に音が再生されることがほぼなくなった。

最後に

むーりぃー
f:id:nixeneko:20161228020555p:plain

初めてのTrueType命令: Windowsでは見えないフォントをつくる

TrueType命令を利用して何も表示されないフォントを作った。
f:id:nixeneko:20161216002924p:plain

フォント概要

作成したフォントは、アウトライン情報は保持しているのだが、TrueType命令によってすべての点をグリフの原点へ移動し、描画されるビットマップが存在しない状態にすることで何も表示されなくしている。

Windowsでは何も表示されなくなる一方で、Macではヒント命令を実行しないので普通に文字が表示される。そのため、フォントレンダラがレンダリング時にTrueTypeヒント命令を実行するどうかを確認するために用いることができる。

ダウンロード

作り方・技術解説

まえがき

最近TrueType命令を触っている。
TrueType命令というのは、OpenTypeフォント規格でアウトラインの形式としてTrueTypeを選択した場合に使用できるヒンティング用プログラム言語である。もともとTrueTypeフォント規格に付随していた。
ヒンティングでは主に制御点を動かすことでレンダリングされるビットマップを調整するのだが、この命令セットが妙に高機能なので色々できそうだ、という訳である。

このTrueType命令だが、最近のmacOS (OS X)では単に無視されるらしい。ヒンティング自体が低解像度の画面で可読性の高い表示を行うためのものなので、高密度なretinaディスプレイの下ではお役御免といったところだろうか。

という訳で、それを使って何か作ってみよう、というのがこの記事の目的である。

なお、記事タイトルの「初めてのTrueType命令」というのは私が触るのが初めてだったからであり、初めての人がこの記事を読んでTrueType命令を書けるようになるかは定かでない。

方針

さて、見えないフォントを作ることにする。つまり、アウトライン情報は保持しているのだが、TrueType命令によってすべてを指定した点(グリフの原点)に移動してしまうことで描画されるビットマップが存在しない状態にしよう、というものである。

TrueType命令を実行しない場合にはグリフが表示されるため、レンダリングしている環境でTrueType命令が実行されているかを確認することに使える。前述したようにTrueType命令は最近のMacでは無視されるので、Windowsでは見えないフォントということになる。

Fontforgeのインストール

TrueType命令のデバッグにはFontforgeが使える。最近のWindowsインストーラでインストールされるものにはTrueTypeデバッガを有効にしてコンパイルしてあるもののようなのでインストールすればそのまま使える。今回はWindows版の2016-10-04 Release Installer (.exe)でインストールし使用した。

コンパイル方法については次のページに書いてあるのでもし必要があれば参照されたい。


また、必須ではないのだが、Fontforgeはファイルメニューの環境設定から「TTF」タブの「大変更時に命令を消去」をオフにしておいた方がいいかもしれない。というのは、誤って制御点を追加してしまい編集中の命令が全消去された経験があるからである。
f:id:nixeneko:20161215202944p:plain

使うフォントの用意

さて、制御点を動かすにはTrueType形式のアウトラインが必要である。
今回はM+フォントをダウンロードしてきて弄ることにする。mplus-1p-regular.ttfをFontforgeで開く。

Fontforgeのヒント命令編集機能

メインウィンドウの「ヒント」メニューからヒント命令の編集ができる。
f:id:nixeneko:20161215203613p:plain


また、アウトラインウィンドウの「ヒント」メニューで個別グリフのヒント命令を細かく編集したりデバッグを行うことができる。
f:id:nixeneko:20161215203829p:plain

前準備

さて、まずはヒント命令を削除する。
メインウィンドウの「ヒント」→「Remove Instr Tables」でTrueType命令を全削除できる。

また、M+フォントはなぜかEMサイズが1000なので、2の冪数である1024にしておく。「エレメント」→「フォント情報」からウィンドウを開き、「一般情報」タブで「EMの大きさ」を1024にしてOKを押す。

TrueType命令を書いていく

制御点の移動命令

さて、実際にTrueType命令を書いていこうと思う。

今回は、グリフの全ての点を原点(0.0, 0.0)に移動することがゴールである。
そのためには制御点を移動する必要がある。制御点を移動する命令は豊富にあるが、今回は座標を指定してそこに移動する命令SCFS[]を使うことにする。
仕様によると、命令は次の様になっている。

SCFS[] Sets Coordinate From the Stack using projection vector and freedom vector

Pops c:coordinate value (F26Dot6)
p:point number (uint32)
Pushes なし
Uses zp2, freedom vector, projection vector

座標c, 制御点番号pを受け取り、pで表される制御点を座標cに移動するというものである。制御点はfreedom vectorに沿って移動され、projection vectorに沿った座標がcとなる場所に移される、らしい。

スタック

ところで、TrueType命令を実行するものはスタックマシンである。データはスタックに積み上げられ、命令の引数というのもスタック上に積まれていく。上の表のPopsというのがスタックから取得するデータを表しており、取得した(popされた)データはスタックから取り除かれる。

上の表ではcとpがこの順番でpopされる。すなわち、この命令を実行する前に、スタックトップにはcが、その一つ下にpがある状態になっている必要がある。

                     ↓スタックトップ
 … | 点番号p | 座標c |

スタックにデータを積む(pushする)のは主にPUSHB命令とPUSHW命令を使う。それぞれ、命令に後続する1~8個のByte(8ビット符号なし整数)もしくはWord(16ビット符号あり整数)の値をスタックに積む。積む数値の個数は指定する必要がある(正確には個数によって別命令になっている)。スタックに積む際には数値は32ビットに拡張され、PUSHBでは符号なしの拡張、PUSHWでは符号拡張される。


次の例はFontforgeのTrueType命令編集機能で認識されるように書いたもので、TrueType命令のマニュアルに書かれている表記とも少し異なっている。

PUSHB_3
 1
 2
 3

とすると

              →スタックトップ
 … | 1 | 2 | 3 |

のようにスタックに積まれ、

PUSHW_2
 2456
 -100

とすると

              →スタックトップ
 … | 2456 | -100 |

のように積まれていく。PUSHB_3、PUSHW_2のように命令の右端についている数字はpushする数値の個数を表している。

制御点移動のコード(1)

さて、SCFSを実行するところに戻る。点番号1の制御点を座標0.0に移動するコードは次のようになる。

PUSHB_1
 1
PUSHW_1
 0x0000
SCFS

試しに実行してみよう。

グリフのアウトラインウィンドウの「ヒント」→「ヒント命令を編集」でヒント編集用のウィンドウを開き、「編集」ボタンを押す(ボタンが何も表示されていない場合、ウィンドウサイズを縦に拡大すると現れる)。上のコードをペーストしOKを押す。
次に「ヒント」→「デバッグ」を開く。「グリッド合わせのパラメータ」ダイアログが開くので適当にOKを押す。
f:id:nixeneko:20161215221739p:plain
上図のようにTrueType命令のステップ実行用のインタフェースが現れ、またTrueTypeのパラメータやスタックを表示するウィンドウが開かれる。

ステップ実行のボタンを押していくと命令をひとつづつ実行することができる。実行結果が次である。
f:id:nixeneko:20161215221912p:plain
点番号1の制御点がX座標0.0に移動していることがわかる。


要するに制御点は設定した一つの方向にしか移動できないということである。そのため、x座標軸、y座標軸両方に対して座標を指定して移動するためには、方向を変化させて2度別々に処理をする必要がある。

移動方向切り替え

移動方向を切り替えるコマンドがSVTCAである。

  • STVCA[0]でY軸方向
  • STVCA[1]でX軸方向

に移動ができるようになる。具体的には距離を測る方向(projection vector)と移動する方向(freedom vector)を座標軸に指定するということらしい。

制御点移動のコード(2): 2次元移動

さて、Y軸方向にも移動させてみよう。制御点1を原点(0.0, 0.0)に移動する。コードを次のように書き換える。

STVCA[1]
PUSHB_1
 1
PUSHW_1
 0x0000
SCFS
STVCA[0]
PUSHB_1
 1
PUSHW_1
 0x0000
SCFS

先ほどのコードの前にSTVCAで移動方向を切り替えるものがついて、先にX軸方向、次にY軸方向、と2回繰り返している訳である。

同様に実行してみる。
f:id:nixeneko:20161215223116p:plain
分かりづらいが原点に移動している。


さて、これを制御点の数だけ繰り返せば全ての点が原点に移動し何も表示されなくなるという訳である。
単純に上のコードを、点番号を変化させて制御点の数だけ並べてもいいが、さすがにアホっぽいのと容量が無駄なのでもっと効率化したい。

関数化

ここで関数が登場する。

関数は'fpgm'テーブルにおいて、FDEF命令で定義できる。FDEFはスタックから整数を一つpopして、その番号の関数の定義を行う。関数の終わりはENDFで示される。
例えば関数0の定義だと次のように書く。

PUSHB_1
0
FDEF
 <ここに関数の内容がくる>
ENDF

さて、先ほどの、制御点を指定して原点に移動させる、という機能を関数としてまとめてみたい。
移動する制御点を指定するために制御点番号を入力とする。呼び出されるときにスタックトップが制御点番号であることを期待するということであり、関数から抜ける際には引数として入力された数値はpopされてスタックから取り除かれた状態にする。

つまり、スタックが、コール時には

              ↓スタックトップ
 … | 点番号p |

となり、関数を抜けるときは

    ↓スタックトップ
 … | 

となるように設計する。

関数番号は0として定義してみよう。

PUSHB_1
0
FDEF
STVCA[1]
DUP
PUSHW_1
 0x0000
SCFS
STVCA[0]
PUSHW_1
 0x0000
SCFS
ENDF

ここで、DUPはスタックトップを複製する命令である。以下のようにスタックの中身を考えてみると動作がわかると思う。

PUSHB_1
0
FDEF      /* ...|p| */
STVCA[1]  /* ...|p| */
DUP       /* ...|p|p| */
PUSHW_1   /* ...|p|p|0.0| */
 0x0000
SCFS      /* ...|p| */
STVCA[0]  /* ...|p| */
PUSHW_1   /* ...|p|0.0| */
 0x0000
SCFS      /* ...| */
ENDF

実行前後のスタックは先ほど設計したのと合っている。

今定義した関数0を実行するのはCALL関数を使う。コードは次のようになる。

PUSHB_2
 1
 0
CALL

CALLは関数番号をpopし対応する関数を実行する。このコードでは0は関数番号、1は関数の引数とする制御点番号である。

さて、実際に関数が動くか実行してみよう。前述したとおり関数定義は'fpgm'テーブルに書かないといけない。これはFontforgeのメインウィンドウから「ヒント」→「'fpgm'テーブルを編集...」から設定することができる。ここで先ほどの関数定義を書き込み、OKを押す。
また、グリフのヒント命令を関数を実行する命令に書き換える。
そして実行してみる。
f:id:nixeneko:20161216020353p:plain
先ほどに実行した命令と同じ動作をしているので成功である。

制御点の数だけ繰り返す

さて、これを制御点の数だけ繰り返さないといけない。
単純に指定回数だけ関数の実行を繰り返すならLOOPCALLという命令が使えるのだが、今回はすべての制御点に対して操作を行うため何回目の実行かということを覚えておかないといけないため、条件分岐IFと相対ジャンプJMPRを使って実装することにした*1

疑似コードを次に示す。nmaxはグリフの最大の制御点番号を指定する。

  n_cur = nmax;
loop_label:
  if (ncur >= 0){
    func0(ncur);
    ncur--;
    goto loop_label;
  }

さて、実際にコードを書いてみる。
Aの制御点は最大の制御点番号は11なので、nmaxとして11を指定してやってみる。

PUSHB_1
 11
DUP
PUSHB_1
 0
GTEQ
IF
DUP
PUSHB_1
 0
CALL
PUSHB_1
 1
SUB
PUSHW_1
 -15
JMPR
EIF
POP

GTEQはスタックトップの2項目を比較しその結果をpushする命令である。

GTEQ            →スタックトップ
before …| a | b | 
after  …| a>=b |

a>=bのときは1、そうでないときは0がpushされる。

IFはスタックトップから整数を一つpopし、それが0でないときは次の命令を実行していき、0ならばEIFにジャンプするというものである。

JMPRは整数を一つpopし、それによって指定される相対位置へとジャンプする。マイナスだと遡る方向にジャンプする。

さて、同様にスタックの中身を考えてみる。

         /* …|              */
PUSHB_1  /* …|ncur<=nmax|   */
 11
         /* …|ncur|         */ /* JMPRでここに戻ってくる */
DUP      /* …|ncur|ncur|    */
PUSHB_1  /* …|ncur|ncur|0|  */
 0
GTEQ     /* …|ncur|ncur>=0| */ 
IF       /* …|ncur|         */ /* if ncur >= 0 then */
DUP      /* …|ncur|ncur|    */
PUSHB_1  /* …|ncur|ncur|0|  */
 0
CALL     /* …|ncur|         */ /* func0(ncur) */
PUSHB_1  /* …|ncur|1|       */
 1
SUB      /* …|ncur-1|       */ /* ncur--; */
PUSHW_1  /* …|ncur-1|-15|   */
 -15
JMPR     /* …|ncur-1|       */ /* goto loop_label */
EIF
POP      /* …|              */

良さそう。さてこのプログラムをグリフのTrueType命令に書き込んで実行してみる。
実行しながらスタックの中身を示すウィンドウを見ていくと、スタックの一番下にある値がひとつづつ減少していき最終的に-1となってループを抜けていることがわかる。
f:id:nixeneko:20161216222646p:plain
描画されるビットマップが何もないという状態になっている。成功である。

ループを関数化

さて、これも関数にする。
スタックがコールする前後で、

              →スタックトップ
before:  … | nmax |
after:   … | 

となるようにしよう。nmaxにはグリフの最大の制御点番号を指定する。
関数番号は1とする。

PUSHB_1
 1
FDEF
DUP
PUSHB_1
 0
GTEQ
IF
DUP
PUSHB_1
 0
CALL
PUSHB_1
 1
SUB
PUSHW_1
 -15
JMPR
EIF
POP
ENDF

これは単純に前のコードから、

PUSHB_1
 11

を除いて、

PUSHB_1
 1
FDEF
 ~
ENDF

で囲んだだけである。
これを'fpgm'テーブルに追記する。

さて、関数1をつくったのでグリフから呼ぶ必要がある。
Aの最大の制御点番号は11なので、11を引数として関数1を呼び出す。コードは次のようになる。

PUSHB_2
 11
 1
CALL

実行してみると次のようになる。
f:id:nixeneko:20161216222403p:plain
関数化前と同様に何も塗られるビットマップがないので成功である。


さて、ここではAにだけ消えるようなヒント命令を実装したが、これをすべてのグリフに対しても適用したい。
そこで、ひとまずフォントを出力する。

フォントを出力

フォント出力前に「ヒント」→「'maxp'テーブルを編集...」からmaxpテーブルの数値を編集する。
f:id:nixeneko:20161216000103p:plain

  • ゾーンは2固定。
  • ストレージは使ってないので0。
  • FDEFはさっき関数0, 1の2つを定義したので2としておこう。
  • トワイライトポイントは使ってないので個数0。
  • スタックの最大深さは……使ってたの5個位だった気がするけど多めに指定しておくか。
  • IDEFは使ってないので0。

とか指定する。使ってるインデックスがそれぞれの指定した値を超えるとうまく動かないことになるのだと思う。

フォント情報(エレメント→フォント情報)を適宜修正し、TrueTypeフォントを出力する(ファイル→フォントを出力)。
f:id:nixeneko:20161216000955p:plain

TTXでXMLに変換

さて、これをTTXを使ってXMLファイルに展開する。
TTXのインストールについては次のサイトを参考にされたい。

今回使用したTTXのバージョンは2.5である。
コマンドプロンプトなどで先ほど出力したフォントAdisappear.ttfがあるフォルダへと移動し、

ttx Adisappear.ttf

と指定して展開し、Adisppear.ttxを得る。

すべてのグリフに適用

Adisappear.ttxをテキストエディタで描き、Aのグリフを探し、紐づいているTrueType命令を調べる。<glyf>~</glyf>の中で<TTGlyph name="A">~</TTGlyph>がある。

    <TTGlyph name="A" xMin="36" yMin="0" xMax="674" yMax="748">
      <contour>
        <pt x="36" y="0" on="1"/>
        <pt x="309" y="748" on="1"/>
        <pt x="401" y="748" on="1"/>
        <pt x="674" y="0" on="1"/>
        <pt x="587" y="0" on="1"/>
        <pt x="511" y="218" on="1"/>
        <pt x="196" y="218" on="1"/>
        <pt x="121" y="0" on="1"/>
      </contour>
      <contour>
        <pt x="218" y="284" on="1"/>
        <pt x="488" y="284" on="1"/>
        <pt x="354" y="674" on="1"/>
        <pt x="352" y="674" on="1"/>
      </contour>
      <instructions><assembly>
          PUSH[ ]	/* 2 values pushed */
          11 1
          CALL[ ]	/* CallFunction */
        </assembly></instructions>
    </TTGlyph>

<instructions><assembly>~</assembly></instructions>に挟まれているのがAのグリフに対応する命令であり、その中の11がAの最大の制御点番号である。これはほかのグリフに適用する際、グリフに含まれる制御点<pt>の個数を数えて、(制御点の個数 - 1)個を指定すればよい。


これをすべてのグリフに対しても適用するようなpythonスクリプトを作成した。

#!/usr/bin/env python3
# conding: utf-8

import xml.etree.ElementTree as ET

INFILE = "Adisappear.ttx"
OUTFILE = "Adisappear-out.ttx"
xmltree = ET.parse(INFILE)
xmlroot = xmltree.getroot()

for glyph in xmlroot.find('glyf').findall('TTGlyph'):
    cnt = 0
    for contour in glyph.findall('contour'):
        cnt += len(contour.findall('pt'))
    if cnt > 0:
        prog ="""
          PUSH[ ]    /* 2 values pushed */
          {} 1
          CALL[ ]    /* CallFunction */
          """.format(cnt-1)
        glyph.find('instructions').find('assembly').text = prog

with open(OUTFILE, 'w') as w:
    w.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    xmlstr = ET.tostring(xmlroot, method='xml', encoding="unicode")
    #xmltree.write(OUTFILE)
    w.write(xmlstr)

やってることは各<TTGlyph>に対して<pt>の個数を求めて、もし個数が1以上なら(個数 - 1)を埋め込んだTrueType命令を<instructions><assembly>~</assembly></instructions>間に挿入するというものである。

これをapplyall.pyという名前でAdisappear.ttxと同じフォルダに入れ実行する。pythonは3系でないとうまく動かない。

python3 applyall.py

実行するとAdisappear-out.ttxが出力されるので、これをTTXを使って.ttfファイルへと変換する。

ttx Adisappear-out.ttx

結果

最終的に得られるフォント(Adisppear-out.ttf)をWindows 10でプレビューしたのが次である。
f:id:nixeneko:20161216002924p:plain
やったー! 何も見えないよ……。

他のOSで見てみると……

Mac OS X 10.5 Yosemite

f:id:nixeneko:20161217153953p:plain
噂通りヒンティングはされていないようである。
まあRetinaだし、ヒントでアウトラインを歪めなくてもオリジナルのアウトラインを綺麗に表示できるので……。

Ubuntu Desktop 16.04

f:id:nixeneko:20161216224852p:plain
ヒント効いてない。

どうやら、標準ではヒントが効かなくなっているらしい?

このページによると、gnome-tweak-toolを使ってヒンティングを調整できるようである。
f:id:nixeneko:20161216225417p:plain
「フォント」タブにて、ヒンティングがデフォルトではSlightになっているところをFullに変更する。

そしてフォントビューアを起動しなおすと……
f:id:nixeneko:20161216225543p:plain
やった!

参考サイト

*1:とはいえ、関数の中にカウンタを用意すればLOOPCALLでも問題なくできる気がする。

UbuntuでFontforgeをコンパイル

Ubuntuならapt一発でFontforgeがインストールできるものの、パッケージマネージャで入れられるものにはTrueType命令のデバッガが有効になっていないため、TrueTypeデバッガを使いたければ自前でコンパイルする必要があります。(Appleのヒンティングの特許が2010年に切れたので現在では標準のリポジトリに入ってるものでTrueTypeデバッガが有効になっています。)

この記事はUbuntuにおける、TrueTypeデバッガを有効にしたFontforgeコンパイルの手順を示します。
基本的には書いてあるコマンドを端から実行していけばインストールできるはずです。

環境

OS: Ubuntu 16.04 LTS 日本語 Remix

コンパイル・インストール手順

パッケージのインストール

次のパッケージをインストールします。

  • git
  • m4
  • libtool
  • autoconf
  • automake
  • libglib2.0-dev
  • libgio2.0-cil-dev
  • libfreetype6-dev
  • libxml2-dev
  • libspiro-dev
  • libtiff5-dev
  • libuninameslist-dev
  • libgif-dev
  • libreadline-dev
  • libzmq-dev
  • python-dev
  • potrace
  • libcairo2-dev
  • libpango1.0-dev

端末上で実行するコマンドだと

sudo apt install git m4 libtool autoconf automake libglib2.0-dev \
libgio2.0-cil-dev libfreetype6-dev libxml2-dev libspiro-dev libtiff5-dev \
libuninameslist-dev libgif-dev libreadline-dev libzmq-dev python-dev \
potrace libcairo2-dev libpango1.0-dev

とか。

Fontforgeソースコードの準備

cd ~
git clone git://github.com/fontforge/fontforge.git
cd fontforge
git checkout tags/20161005

git checkoutのところで、リリースを指定します。
Releases · fontforge/fontforge · GitHub
ここなんかを参照しながら好きなリリースを選ぶといいと思います。ここでは最新のリリースである20161005にしています。

Freetypeソースコードの準備

http://download.savannah.gnu.org/releases/freetype/
ここからFreetypeのソースをダウンロードしてきて展開します。

今回はインストールされているlibfreetype6パッケージとバージョンを合わせて2.6.1としました。

wget http://download.savannah.gnu.org/releases/freetype/freetype-2.6.1.tar.gz
tar zxvf freetype-2.6.1.tar.gz 

Fontforgeコンパイル

./configureで--enable-freetype-debuggerオプションをつけ、Freetypeソースのディレクトリを指定することで、TryeType命令のデバッガを有効にしています。

./bootstrap
./configure --enable-freetype-debugger=freetype-2.6.1/
make
sudo make install

Fontforgeがうまく起動できない場合は一度Ubuntuを再起動します。

CygwinでFontforgeをコンパイル

TrueType Instruction用のデバッガがFontForgeには載ってるのだが、有効にするためには自前でコンパイルしないといけないらしい。

という訳で、Cygwinからやってみる。

環境

Fontforgeコンパイル

fontforge/CONTRIBUTING.md at master · fontforge/fontforge · GitHub
ここに書いてある通りにしてコンパイル等をする。

準備

まずはSetup.exe(Setup-x86_64.exe)を使って必要なパッケージをインストール。手元の環境では次のパッケージが追加で必要になった。今までにいろいろ入れてるので更に必要であると思う。

  • m4
  • libtool
  • autoconf
  • automake
  • libspiro-devel
  • libtiff-devel
  • libuninameslist-devel
  • giflib
  • libgif-devel
  • libreadline-devel
  • libzmq-devel
  • python-devel
  • gettext-devel
  • libpango1.0-devel
  • libxml2-devel
  • libiconv-devel

ソースをダウンロード:

  • libfreetype6

また、Cygwinを使ってFontforgeを入れてた場合はアンインストールしておく。

コンパイル&インストール。

基本的には一般的な手順だが、./configureの前に./bootstrapを実行し依存性チェックやconfigureファイルの生成をする必要があるようだ。
また、 ./configure には --enabele-freetype-debugger オプションの設定とFreetypeのソースの入っているディレクトリを設定することでTrueTypeデバッガが有効になるらしい。
./checkoutや./configureの様子を見ながら足りないパッケージは適宜インストールする。

$ cd ~
$ mkdir ff
$ cd ff
$ git clone git://github.com/fontforge/fontforge.git
$ cd fontforge/
$ git checkout tags/20161005
$ ./bootstrap
$ cd ..
$ cp /usr/src/freetype2-2.6.5-1.src/freetype-2.6.5.tar.bz2 .
$ tar jxvf freetype-2.6.5.tar.bz2
$ cd ../fontforge
$ ./configure --enable-freetype-debugger=../freetype-2.6.5
$ make
$ make install

libzmq-devel等をインストールしたものの、./configureが言うには、zeromq (libzmq)のHave?がnoのまま。つまり認識されていない?とりあえずoptionalなライブラリなので無くてもなんとかなるようだが。


cygwinからstartxwinなどでxtermを起動してfontforgeを実行すると無事に起動した。めでたしめでたし。

(2016-11-10 一部修正)

キングブレードX10III Neo分解

f:id:nixeneko:20161016180341j:plain
キンブレ買ったので分解した。分解手順を記録しておく。実際には組み立てていくところを逆から追っていった感じ。

分解したのは、キングブレードX10III Neoのスモーク。


まず先端についてる筒を外す。
f:id:nixeneko:20161016180641j:plain
f:id:nixeneko:20161016180958j:plain

この状態では懐中電灯として使える。
f:id:nixeneko:20161016181007j:plain


リングを外す。なかなか外れず苦労したが、足で柄を抑えて下の縁に爪をひっかけて上に引っ張ったら外れた。うまい具合に上方向に安定した力をかけられるなら何でもいいのだろうが。
f:id:nixeneko:20161016181621j:plain

本体側に溝が掘ってあり、リング裏の出っ張りとかみ合ってリングが回転しないようになっている。そのためリングを外す際にはまっすぐ上に引っ張る必要がある。
f:id:nixeneko:20161016181630j:plain
f:id:nixeneko:20161016182046j:plain


電池蓋を外し、電池を外す。
f:id:nixeneko:20161016182543j:plain

電池が入るところの下部にあるネジ2つを外す。
f:id:nixeneko:20161016182831j:plain


横側の溝にマイナスドライバーの先を突っ込んでこじ開ける。ネジで留まっていた下の方はある程度開くのでそこから入れて少しずつ広げていくといいかもしれない。失敗すると傷がつく(ついた)。逆側も同様にして開くと覆いが外れる。
f:id:nixeneko:20161016183445j:plain

こんな風に切り欠きと出っ張りで嚙み合わさっている。
f:id:nixeneko:20161016183603j:plain
f:id:nixeneko:20161016183743j:plain


開けたところ(先頭部分)。
f:id:nixeneko:20161016184431j:plain

光を均一にするためのレンズを取り外す。LED基盤と一緒に溝にはまってるだけなので引き出す。
f:id:nixeneko:20161016184649j:plain

外したレンズ。
f:id:nixeneko:20161016185002j:plain
f:id:nixeneko:20161016185014j:plain


レンズ外す際に基盤も一緒に外れるが、ここでは嵌めなおしてある。
f:id:nixeneko:20161016184027j:plain
f:id:nixeneko:20161016190140j:plain

下のスイッチ側も外す。ここもスイッチ用の基盤が溝にはまっているだけなので引けば外れる。
f:id:nixeneko:20161016190353j:plain
はんだごてを使わない分解はこれにて終了。


LED基盤上部はこんな感じ。RGBWのフルカラーLEDを搭載している。基盤の裏はアルミ板か何かになっているようで、放熱用だろうか。
f:id:nixeneko:20161016190735j:plain

制御基盤? LED基盤と半田で垂直に固定されている。
f:id:nixeneko:20161016191043j:plain

制御基盤逆面。
f:id:nixeneko:20161016191224j:plain

記事内の画像のライセンス

この記事の画像はCC0ライセンスにより提供されます(2017/03/19~)。

CC0
To the extent possible under law, nixeneko has waived all copyright and related or neighboring rights to キンブレ分解写真. This work is published from: 日本.

さくらのサーバに置いたWebフォントをはてなブログから使う

さくらのレンタルサーバ上にWebフォントのファイルを置いて、それを外部のブログやサイトから参照できるようにする話。

経緯

今年の8/31をもってGoogle DriveのWebホスティングサービスが終了したので、Google Drive上に上げていたWebフォントが使えなくなった。
という訳でさくらのレンタルサーバ上に移行しようとしたのだが、フォントファイルをサーバ上に移動させたものの、はてなブログ(このブログ)からはフォントファイルが読み込まれなかった。

問題点

どうやらクロスドメインによるアクセスが問題のよう。
具体的にはHTTPレスポンスヘッダに“Access-Control-Allow-Origin”がないといけないらしい。
Access-Control-Allow-Originは、それによって指定されたドメインからのアクセスなら読み込んでいいよってものらしい。"*"を指定するとどんなドメインからのアクセスでも読み込めるようになる。

解決方法

現在のさくらのレンタルサーバでは.htaccessでHeaderが効くらしいので、

.htaccessファイルを作成し、

Header set Access-Control-Allow-Origin: "*"

などと書き込んでフォルダと同じディレクトリにぶち込むととりあえずはてなブログからも読み込めるようになる。
セキュリティを考えるとちゃんとドメインを指定したり、フォントに限定して設定できるよう工夫した方がよさそう。