2016年5月28日土曜日

フォントの高さの修復方法

TeXで使われているフォントをWindowsで使おうとすると、
行の高さ(line-space)が壊れていることがあるので修復してみた。

TeXで使われているフォントは、
https://www.ctan.org/
で入手できるものが多い。

TeXでデフォルトで使われるフォントはComputer Modern Unicode
https://www.ctan.org/pkg/cm-unicode
で、このフォントの高さは正常なのでそのままWindowsのフォントとして使用できる。

しかし、すべての文字についてデフォルトがComputer Modernだというわけではない。
数式で使用されるギリシャ文字にはLatin Modern Math (LM Math)
http://www.gust.org.pl/projects/e-foundry/lm-math
https://www.ctan.org/tex-archive/fonts/lm-math
が用いられている。

残念ながら、この記事を書く時点では、このLatin Modern Mathの
フォントファイルに記録されている行の高さ情報は正しくなく、
上下におよそ3行分ずつ余分な空白が入ってしまう。
WordやLibreOfficeで使おうとするととても使いにくい。


そこで、FontForge
https://fontforge.github.io/en-US/
を使ってフォントの高さの修復を行う。

なお、FontForgeで検索すると日本語で書かれている
http://fontforge.github.io/ja/
がヒットするが、そこからたどりつく
https://sourceforge.net/projects/fontforge/files/fontforge-executables/
にある
FontForgeSetup_2012-07-31_Windows.exe
は使わないほうがよい。古く、日本語が正常に表示されない。

一方、Cygwin版
fontforge_cygwin-20090914.tar.bz2
なら表示が崩れることはない。しかし、fontforgeのGUI表示用にCygwinのXが必要になるので
CygwinのXをインストールしなければならず、また、FontForge自体が古い。


さて、FontForgeの準備ができたら、起動して修正したいフォントを読み込み、
次の手順にしたがってフォントを修正する。

  1. エレメント→フォント情報と進み、設定画面を表示させる。Cygwin版ならElement→Font Infoと進む。 
  2. 左側の一覧から「一般情報(General)」を選択する。 
  3. 高さ(Ascent)と深さ(Descent)の値を覚える。
  4. 左側の一覧から「OS/2」を選択し、「メトリック(Metrics)」タブを選択する。
  5. 「オフセットを指定(Is Offset)」のチェックボックスをすべてはずす。
  6. Win AscentとWin Descentの値をそれぞれ3.で覚えた値に設定する。
  7. 組版上の高さ(Typo Ascent)とhheaテーブルでの高さ(HHead Ascent)を3.で覚えた高さの値に設定する。
  8. 組版上の深さ(Typo Descent)とhheaテーブルでの深さ(HHead Descent)を3.で覚えた深さの値を負にした値に設定する。例えば、深さが194であれば-194を設定する。
  9. OKボタンを押す。
  10. ファイル(File)→フォントを出力(Generate Fonts)と進む。
  11. 出力形式を指定する。今、修正しようとしているLatin Modern MathはOpenTypeフォントなので、OpenTypeを選択する。
  12. 生成ボタンを押す(フォントによっては途中で警告表示がでるかもしれない)。
  13. 生成したフォントファイルのプレビュー(エクスプローラで右クリック→プレビュー)を表示すると、無駄な行間スペースがなくなっていることが確認できる(はず)。

以上で行の高さを修正したフォントが得られるので後はシステムにインストールするだけである。

フォントの高さの設定に関しては、以下を参考にした。
http://fontforge.github.io/faq.html#linespace
https://fontforge.github.io/ja/fontinfo.html#TTF-Metrics
http://designwithfontforge.com/en-US/Line_Spacing.html

2016年5月15日日曜日

d3.jsで横向きの棒グラフ

JavaScriptで棒グラフを作成してみた。
d3.jsを使う。使い方は、主に、
http://ja.d3js.info/alignedleft/tutorials/d3/
を参考にした。

まずはデータを準備する。ファイル名は、test.datとする。
スペース区切りのデータである。1列目が項目名、2列目が棒グラフにする値である。
3列目は棒にマウスが移動すると表示される値とする。

Tokyo 300 0.1
Kyoto 100 0.5
Osaka 150 1.2
Nagoya 200 0.3
Yokohama 250 0.7
このデータから横向きの棒グラフ

を作成する。

そのためのHTMLファイルは以下の通りである。
ファイル名は任意で、test.datと同じディレクトリに作成する。
このファイルをブラウザで表示すると、横向きの棒グラフが表示される。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>D3 Test</title>
    <script type="text/javascript" src="d3/d3.js"></script>
    <style type="text/css">
      .axis path, .axis line {
        fill: none;
        stroke: black;
      }
      .axis text {
        font-family: sans-serif;
        font-size: 11px;
      }
    </style>
  </head>
  <body>
    <script type="text/javascript">
      var w = 500; // Width of a SVG canvas
      var h = 100; // Height of a SVG canvas
      var axisHeight = 20; // Height of a horizontal axis
      var barPadding = 3;  // Padding between bars
      var svg = d3.select("body")
        .append("div")
        .append("svg")
        .attr("width", w)
        .attr("height", h);
      d3.text("./test.dat", function(error, text) { // load a data file
        // Parse space-separated values
        var data = d3.dsv(" ", "text/plain").parseRows(text, function(d) {
          return { name: d[0], value: d[1], ratio: d[2] }; // Parse one line
        });
        
        // Make a tooltip showing data.ratio as text when mouseover
        var tip = svg.append("text")
          .attr("font-size", "11px")
          .attr("text-anchor", "start")
          .attr("dominant-baseline", "middle")
          .attr("visibility", "hidden");

        // Draw region of y-label
        var yLabelOffset = 5; // Padding between a label and a bar
        var yLabelMaxWidth = 0;
        var yLabel = svg.selectAll("text")
          .data(data, function(d, i){
            if(d === undefined){
              return i;
            }
            return i+1;
          })
          .enter()
          .append("text")
          .text(function(d) {
            return d["name"];
          })
          .attr("y", function(d, i){
            return (i+0.5)*(h-axisHeight)/data.length;
          })
          .attr("font-size", "11px")
          .attr("text-anchor", "end")
          .attr("dominant-baseline", "middle")
          .each(function(d){
            yLabelMaxWidth = Math.max(this.getBBox().width, yLabelMaxWidth);
          })
          .attr("x", yLabelMaxWidth);
        yLabelMaxWidth += yLabelOffset;

        // Make a horizontal scale
        // (last '-10' makes room for drawing axis labels)
        var xScale = d3.scale.linear()
          .domain([0, 500])
          .range([0, w - yLabelMaxWidth - 10]);
        
        // Make an axis with xScale
        var xAxis = d3.svg.axis().scale(xScale).orient("bottom");

        // Make horizontal bars
        svg.selectAll("rect")
          .data(data) // Load data
          .enter()    // Make placeholders
          .append("rect")
          .attr("x", yLabelMaxWidth)
          .attr("y", function(d, i) {
            return i*(h - axisHeight)/data.length + barPadding/2;
          })
          .attr("width", function(d){return xScale(d["value"])})
          .attr("height", (h - axisHeight)/data.length - barPadding)
          .attr("stroke", function(d) {
            return "rgb(0, 0, " + (d["value"]) + 50 + ")";
          })
          .attr("stroke-width", 1)
          .attr("fill", function(d) {
            return "rgb(0, 0, " + (d["value"]) + ")";
          })
          .on("mouseover", function(d, i){
            return tip.attr("visibility", "visible")
            .text(d["ratio"])
            .attr("x", yLabelMaxWidth + xScale(d["value"]) + 3 + "px")
            .attr("y", ((i + 0.5)*(h - axisHeight)/data.length) + "px");
          })
          .on("mouseout", function(d){
            return tip.attr("visibility", "hidden");
          });
        svg.append("g")
          .attr("class", "axis")  // Define the class "axis"
          .attr("transform", "translate(" + yLabelMaxWidth + ","
            + (h - axisHeight + barPadding/2) + ")")
          .call(xAxis);
      });
    </script>
  </body>
</html>