読者です 読者をやめる 読者になる 読者になる

だるろぐ

とてもだるだるした日記です http://about.daruyanagi.jp/

お知らせ

WebMatrix 2: 縦書きツイートするためのアプリ作った

WebMatrix ASP.net Web Pages Twitter

f:id:daruyanagi:20130315025326p:plain

Twitter の Web UI で改行が使えるようになったとのことで、タイムラインが縦書きでいっぱいです。それをみていたら、自分も漢詩なんかが縦書きで投稿できるアプリがほしくなりました。

Inside Tategaki

拡張メソッド

さっそく中身を紹介したいのですが、その前に拡張メソッドをいくつか用意しておきます。クラス名が体を表していないのは見逃してください。拡張メソッドのクラス名なんか飾りなんですよ!

  • Transpose() : 行と列を逆にします
  • Join() : string.Join() を "hoge".Join() で呼び出す
  • Times():string * int がほしかった

~/App_Code/EnumerableExtensions.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public static class EnumerableExtensions
{
    public static IEnumerable<IEnumerable<T>> Transpose<T>(
        this IEnumerable<IEnumerable<T>> source)
    {
        return from row in source
               from col in row.Select(
                   (x, i) => new KeyValuePair<int, T>(i, x))
               group col.Value by col.Key into c
               select c as IEnumerable<T>;
    }

    public static string Join<T>(
        this IEnumerable<T> source, string seperator)
    {
        return string.Join(seperator, source);
    }

    public static string Times(this string source, int times)
    {
        return Enumerable.Range(0, times).Select(_ => source).Join("");
    }
}

メインページ

“既定のページの管理”で .cshtml を追加してあります。

~/Tategaki/Default.cshtml

@{
    var text = (Request["text"] ?? string.Empty);
}

<div style="width: 300px; margin: 0 auto;">
    <form action="">
        <p><textarea name="text" style="width: 100%; height: 10em;">@text</textarea></p>
        <p style="text-align: right;"><input type="submit" /></p>
    </form>

@{
    text = text.Replace("  ", " ").Replace(" ", " ");
    text = Microsoft.VisualBasic.Strings
        .StrConv(text, Microsoft.VisualBasic.VbStrConv.Wide, 0);
    
    var s = Request["s"].AsInt(1);
    var new_line = new [] { "\r\n", "\r", "\n" };
    var lines = Html.Encode(text).Split(new_line, StringSplitOptions.None);
    var width = lines.Max(_ => _.Length);
    
    var vertical_text = lines
        .Reverse()
        .Select(_ => _.PadRight(width, ' ').ToCharArray().AsEnumerable())
        .Transpose()
        .Select(_ => _.Join(" ".Times(s)).TrimEnd())
        .Join("\n") + "\n\n";
}

半角 ⇒ 全角 の変換処理はめんどくさいの Visual Basic の機能(StrConv 関数)を引っ張ってきました。Web.config を書き換えて DLL の参照を追加するより、一度「Visual Studio」で開いて[参照の追加]を使うのが簡単。

AsInt() は ASP.NET Web Pages で提供されている便利なメソッド。ここでは Request[] で受け取った値を Int32 型にしている(失敗したら 0、引数を与えれていればデフォルト値を返す)。ほかにもいろんな型に対応するメソッドが用意されてるよ!

手抜き命名の変数 s は行間に挿入するスペースの数(LINQ の下から二つ目 " ".Times(s) の部分)。

あとは LINQ で

  1. 横にスライス(縦書きの時は右上始点で読むのリバースしておく)
  2. それをまた縦にスライスして char の表に(空きは簡便のため全角空白で埋める)
  3. 行と列を入れ替えて縦書きに
  4. 各行の char[] を連結(うしろの空白は不要なのでカット)
  5. すべての行を連結

するだけ。もっといい方法あるかな?

@functions
{
    public string GetShorURL(string url){ 
        string endpoint = "https://www.googleapis.com/urlshortener/v1/url"; 
        string json = "{\"longUrl\": \"" + url + "\"}"; 
        WebClient client = new WebClient { Encoding = System.Text.Encoding.UTF8 }; 
        client.Headers["Content-Type"] = "application/json";
        var response = Json.Decode(client.UploadString(endpoint, json));
        return response.id as string;
    }
}

@{
    var url = GetShorURL(Request.Url.AbsoluteUri);
}
    
    <pre style="padding: 1em; background-color: whitesmoke; border: 1px lightgray solid; overflow: auto;">@vertical_text</pre>
   

    <div style="text-align: right;">
        <span>@(140 - vertical_text.Length)</span>
    </div>

    <div>
        <a href='https://twitter.com/intent/tweet?text=@HttpUtility.UrlEncode(vertical_text)' class="twitter-mention-button" data-lang="ja" data-size="large" data-related="daruyanagi" data-url="@url">Tweet</a>
        <script>!function (d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (!d.getElementById(id)) { js = d.createElement(s); js.id = id; js.src = "//platform.twitter.com/widgets.js"; fjs.parentNode.insertBefore(js, fjs); } }(document, "script", "twitter-wjs");</script>
    </div>
</div>

goo.gl で短縮するための処理を入れている。リクエストを Json で扱うのだけど、エンコード・でコードするクラスがあるのが便利だった……

f:id:daruyanagi:20130315035335p:plain