2018年7月30日月曜日

HTML 枠内にできるだけ大きめの表示。大きければ縮小して全体を表示

WEBページ表示で、「Excel のように縮小して全体を表示したい」という衝動には何度も駆られますね。
住所表示用の枠を用意していて、通常はうまいこと表示できるけど長い住所だとはみ出してしまう、とか。
あともう一つ。「枠内にできるだけ大きく表示させたい」というのもあります。
PDFの編集領域とか宛名ラベル用のアプリケーションなどにそういった枠があります。
今回はその両方、つまり
『文字列が短いときは枠内にできるだけ大きく表示』
『文字列が長いときは枠内に縮小して全体を表示』
という2つの要求を満たしたい、という仕様です。
まぁ印刷する必要のあるページなどには必須だと思います。

WEB上を探していくつかダウンロードしてみましたけど、思うように動いてくれなかったので自作しました。
突っ込みどころあると思いますが、とりあえず動いたので公開しときます。
(きっとたくさんの人がこういった情報を求めているだろうと想像したのでw)

住所表示用に用意した、span id="span_address" のタグに「秋田県北秋田郡上小阿仁村大字沖田面字小蒲野下タ川原」を放り込みたいときのサンプルコードを示します。
(※住所は長い住所で検索したら出てきたもの)

<html>
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script type="text/javascript">
    <!--
      // sample
      window.onload=function()
      {
        $('#span_address').text('秋田県北秋田郡上小阿仁村大字沖田面字小蒲野下タ川原');
        adjust_tagfont();
      }
      // 表示、再表示時に呼び出す
      function adjust_tagfont()
      {
        //はみ出しそうなタグの親idを列挙
        var tags = ['div_address'];
        tags.forEach(adjust_tag);
      }
      // タグの確認
      function adjust_tag(tag, index, array_name)
      {
        if(document.getElementById(tag) !== null)
        { 
            $('#' + tag).autoSizeByWidth();
        }
      }
      // width/height のクラス
      function ssize(_width, _height)
      {
        this.width = _width;
        this.height = _height;
      }
      // 文字列表示の幅と高さを計算する
      function string_size(str, font_size)
      {
        var chk_span = $("#for_check");
        chk_span.text(str);
        chk_span.css('fontSize', font_size + 'px');
        var width = chk_span.outerWidth();
        var height = chk_span.outerHeight();
        chk_span.empty();
        return new ssize(width, height);
      }
      // 
      jQuery.fn.extend(
      {
        autoSizeByWidth: function()
        {
          $this = $(this);    // 枠
          this.each(function()
          {
            $this = $(this);
            var p_width = $this.width()
            var p_height = $this.height();
            var str = $.trim($this.text()); //センタリングなどの処理をはずす
            var fontSize = parseInt($this.css('fontSize').replace('px', ''));
            var child_size = string_size(str);
            // できるだけ大きく
            while((child_size.width < p_width) && (child_size.height < p_height))
            {
              child_size = string_size(str, ++fontSize);
            }
            //はみ出しているとき
            while((p_width < child_size.width) || (p_height < child_size.height))
            {
              child_size = string_size(str, --fontSize);
            }
            $this.css('fontSize', fontSize + 'px');
          });
          return this;
        },
      });
    -->
  </script>
</head>

<body>
  <span id="for_check" style="visibility:hidden;position:absolute;white-space:nowrap;">
  </span>
  <table width="400" border="1">
    <tr>
      <td width="300" height="50">
        <div id="div_address">
            <span id="span_address">&nbsp;</span>
        </div>
      </td>
      <td width="100">
        &nbsp;
      </td>
    </tr>
</body>
</html>

table タグで物理的な大きさを指定してます。
任意のサイズにして確認してみてください。

いちおう解説しますと・・・
まず枠の大きさを取得します。
フォントサイズを拡張して枠からはみ出すところまで確認した後で枠の中に納まるようフォントサイズを縮小していきます。
これだけなんだけど、まわりっくどいっすねw

お役に立てれば幸いです。



2018年7月25日水曜日

Perl CGI でバイナリファイルのアップロードに失敗する

use CGI;
my $cgi = new CGI();
my $fh = $cgi->upload(file);
my $temp = $cgi->tmpFileName($fh);
File::Copy::move($temp, $target_filename);

これでうまくいくはずなんですよね。
png ファイルをアップロードしても表示されない・・・
なかなかはまってしまいました。
コピーされたファイルのサイズが大きくなってるんです。

ファイルの中身をバイナリダンプしてみると、EF BF BD というバイト列が多数。
つまりバイト単位に変換できてないので無効な情報とされているわけ。

ネット上を検索していろいろ試しました。
binmode(STDIN);
とか
binmode(STDIN, ':raw');
とか
コピーを実装したりとか。

で、何時間も試行錯誤して、ふとスクリプトの上部を見てみると
use Encode;
use encoding 'utf-8';

ここで入力がすべてUTF-8 とみなされてしまい、バイナリ値が FF BF BD に変換されてしまっていたわけです。
この2行、削除したらまんまと動いてくれました。