FreeBSD 11 on Azure のポイント

いやまぁ 10.3-R とかでも立ち上げてたけど、11.0-R でごりっと立ち上げたりした上でのポイントまとめとして。

  • とりあえず最初に pkg upgrade はしよう
  • sshd の listen port 変える場合は穴開けよう
  • パスワード設定されてないので wheel への追加と sudo passwd と sudo passwd (user) はしておこう
  • /etc/localtime はないよ

イメージを展開した直後にインストールされているパッケージはちょっと古いので、やっぱり pkg upgrade は必要な感じですね。

freebsd-version は 11.0-RELEASE-p10 だったから問題ないのだけど、うーん。(笑)

「security log 的にうぜー」という気持ちで sshd の listen port を変える場合、Linux 仮想マシン (扱い) は基本的に 22 番しかポート開いてないので、しっかり開けましょう。大事。

deploy 直後のイメージだと、(おそらく普通に作る) SSH 公開鍵認証の場合、ユーザーのパスワードは「非公開」とかになってるので、しっかりパスワードを設定しておかないと……うっかり freebsd-update upgrade のついでに pkg delete -af とかしたい気分になったときに「ちょ、sudo 使えない!」でハマります。wheel にも入ってないので、当然 su - root とかもできません。

「その時は仮想マシンごと壊せばいいや」で済むならともかく、済まなそうな事をするなら、しっかり sudo passwd で設定しておきましょう。

済む形にまとめられるのが (クラウド環境的には) 最善だとは思いますが、一応。

/etc/localtime がない (= 基本 UTC) ってのは、共通イメージ的には基本だし当然ではあるのだけど、穴と言えば穴なので、気にしておかないとハマる点ではあります。

/etc/mail/aliases の root なりをしっかり書き換えてホストレポート受信してれば「あれ、こんなタイミングで来るの?」とかで気付きやすい点ではありますが。

localtime をしっかり見て欲しいなら (日本の場合は) 'sudo cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime' ですね。(/usr/share/zoneinfo 以下は必要に合わせて調整で)

実機デバッグが適切に動作しないとか……

UWP でちょこちょこっとアプリを書いたりしていると、たまにローカルコンピューター上でのデバッグ実行でも、Mobile Emulator (on Hyper-V) 上のデバッグ実行でも全然問題ないのに、MADOSMA などの ARM 機にデプロイしてデバッグ実行するとスコンスコンと落ちまくるパターンがあったりします。

コード見直しても何の問題もないのに、本当にスコンスコンと落ちて、割と意味が分かりません。

で、ちょこっと調べてみたところ……。

Debug/ARM でビルドして配置すると、win10-arm-aot ではなくwin8-arm-aot になっていて、この場合、(Win10 系として存在するはずの) ライブラリーの一部が存在せず、その結果、スコンと落ちる、と。

……えー。

で、Release/ARM の場合は win10-arm 系になり、こちらには含まれているので、落ちたりせずに動作する、と。

……えー。

これは、UWP アプリは基本的に x86 環境で開発して、ARM 環境は Release ビルドでパフォーマンス調整とかその辺頑張れ的な扱いをする以外ないのかなー?

実機動作でのパフォーマンスプロファイルって、どうやるのが一番いいんだろうなー、これ。

NPOI で xlsx を出力すると、なぜか下線が引かれてしまう

NPOI 2.2.1 で 2007 形式の Excel ファイル (.xlsx) を出力した際、なぜか文字にすべて下線が引かれてしまうため調べてみた結果、以下のような問題を発見しました。(PR 済み)

  • XSSFFont の下線情報の設定処理がおかしい
  • FontUnderline.NONE が内部値がおかしい

この合わせ技によりバグが発生します。

まず XSSFFont.cs の該当部実装がこちら。

internal void SetUnderline(FontUnderlineType underline)
{
    if (underline == FontUnderlineType.None && _ctFont.sizeOfUArray() > 0)
    {
        _ctFont.SetUArray(null);
    }
    else
    {
        CT_UnderlineProperty ctUnderline = _ctFont.sizeOfUArray() == 0 ? _ctFont.AddNewU() : _ctFont.GetUArray(0);
        ST_UnderlineValues val = (ST_UnderlineValues)FontUnderline.ValueOf(underline).Value;
        ctUnderline.val = val;
    }
}

新しい値が None で、かつ内部で既に下線の値を保持している場合、内部に保持している下線情報をクリアします。

そして、そうではない場合は既に内部に下線の値を保持している場合はその情報を、保持していない場合は新しい下線情報を作成した上で、指定された値を保持します。

この情報は出力結果となる xslx 内にある styles.xml に u 要素の出力に反映され、値が設定されていない場合 (_ctFont.sizeOfUArray() == 0 の時) は u 要素が出力されず、値が存在する場合はその値が出力されます。

出力される値は FontUnderlineType.cs にある None の値 となりますが、こうなっています。

public static readonly FontUnderline NONE = new FontUnderline(5);

これを踏まえた上で、下線を設定しない状況で明示的に下線なしの指定をしてみます。

var font = ((XSSFWorkbook)workbook).CreateFont();
font.Underline = FontUnderlineType.None;

この状況の場合、font の内部に保持している下線の情報が存在しており、ここに None の値がセットされていることになります。このため、u 要素が出力されます。

これで出力すると styles.xml に <u val="5"/> が出力されますが、Excel 上で見ると下線が引かれてしまうのです。

そして……。

var font = ((XSSFWorkbook)workbook).CreateFont();
font.Underline = FontUnderlineType.None;
font.Underline = FontUnderlineType.None;

SetUnderline() のコードを見た上であれば、これにより何が起きるかは想像できると思います。

そう、この場合は 2 回目の値の設定により、内部の下線情報がクリアされます。

このため、styles.xml に u 要素が出力されません。

つまり、下線なしにしたい場合は None を「設定しない」か、偶数回設定したらいいことになります。そんな馬鹿な!

さて、ここで FontUnderline.cs を少しだけいじってみます。

まず NONE の値を 5 から 0 に。

public static readonly FontUnderline NONE = new FontUnderline(0);

次に、静的コンストラクタの処理をほんのちょっとだけ変更。

static FontUnderline()
{
    if (_table == null)
    {
        _table = new FontUnderline[5];
        _table[0] = FontUnderline.NONE;
        _table[1] = FontUnderline.SINGLE;
        _table[2] = FontUnderline.DOUBLE;
        _table[3] = FontUnderline.SINGLE_ACCOUNTING;
        _table[4] = FontUnderline.DOUBLE_ACCOUNTING;
    }
}

(_table = new FontUnderline[6]; を [5] に、_table[5] だった NONE を [0] に移動しています)

この状態で、先と同じように一度だけ指定して出力してみます。

var font = ((XSSFWorkbook)workbook).CreateFont();
font.Underline = FontUnderlineType.None;

この場合、u 要素は <u val="none"/> と出力されます。あれ?(笑)

Office Open XML の仕様である ECMA-376 の Part 4 にこの辺りの定義が書かれていますが、スプレッドシートのフォントスタイルにおける u 要素の値は以下のようになっています。

sml_ST_UnderlineValues = 
  string "single" 
  | string "double" 
  | string "singleAccounting" 
  | string "doubleAccounting" 
  | string "none" 

そう、"5" はおかしくて "none" が正しいのです。そして、この場合 (当然) 下線は引かれません。(そもそも 0 にすると適切に "none" を出すって……)

ということで、この点を 0 にすると (とりあえず 2007 形式では) 適切な u 要素が出力されるようになります。

しかし、そもそも <u val="none"/> って、出す必要ないよね? という話があります。指定しなければ下線引かれないのですし。なのでこんな感じでいいのでは。

internal void SetUnderline(FontUnderlineType underline)
{
    if (underline == FontUnderlineType.None)
    {
        _ctFont.SetUArray(null);
    }
    else
    {
        CT_UnderlineProperty ctUnderline = _ctFont.sizeOfUArray() == 0 ? _ctFont.AddNewU() : _ctFont.GetUArray(0);
        ST_UnderlineValues val = (ST_UnderlineValues)FontUnderline.ValueOf(underline).Value;
        ctUnderline.val = val;
    }
}

(if の条件を None の場合のみに変更)

……ということで NPOI にこんな感じで PR だしたものの、POI だとどうなっているのだろうって確認してみたのですが、詳しく追いかけてないけど、XSSFFont.javaFontUnderline.java もまったく同じような……。