風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

SVGでテキストの縦位置(baseline)を調整する方法を調べてみた

SVGで、テキストの縦位置(baseline)を揃える処理を書こうとしてはまったので、覚え書き。

経緯

コピィ・ライターを作成していてふと、
「あれ? 基本的にテキストを配置してバナーを作るものだし、SVGと相性良いんじゃ? 設定ファイルを JSON で書き出しているところをちょっといじれば、SVG で書き出すこともできるようになるかも?」
と思い立ったので、処理を追加してみたところ、テキストの baseline を揃えるところで苦労したというもの。
そもそも、SVGをいじるのはこれが初めてなので、誤解をしているところはあるかも……変なところがあればご指摘願いたい。

どこではまったか? 「え……またIEかよっ!」

HTML5 Canvas の場合、textBaseline プロパティを指定すればテキストの baseline が指定できていた。
同じようなものが SVG でも無いか探してみると、text 要素等で指定できる dominant-baseline プロパティがこれに相当するようだ。

HTML5 Canvas と SVG のテキストの baseline の対応(今回対象としたもののみ)
baseline(基底線) Canvas
(textBaselineプロパティ)
SVG
(dominant-baselineプロパティ)
The top of the em square top text-before-edge
The middle of the em square middle central
The bottom of the em square bottom text-after-edge

em square=文字の構造的な大きさを表す四角形、EM box・em quad 等とも。

■参考

textBaseline プロパティ - Canvasリファレンス - HTML5.JP
svg要素の基本的な使い方まとめ


それで、実際やってみたところ……

のように、IE では効いていないように思われた。


それで調べてみると、確かにサポートされていないようだ。

IE9 Mode, IE10 Mode, IE11 Mode, and EdgeHTML Mode (All
Versions)

The dominant-baseline attribute is not supported.

[MS-SVG]: [SVG11] Section 10.9.2, Baseline alignment properties

ではどうするか?

とりあえず、IE でなんとかする方法を探してみたところ、

The specification defines central like that:


central


This identifies a computed baseline
that is at the center of the EM box.

We can take an EM box of known font size and measure its bounding box to compute the center.

internet explorer 9 - How to center SVG text vertically in IE9 - Stack Overflow

とあり、具体的なソースコードが書かれている。


どうやら、SVG中の script 要素にて、EM box の座標と大きさを元に central 相当の位置を決めて、transform 属性にて位置を調整する、ということらしい。

  • SVG 中の text 要素に指定した座標を (Tx, Ty)
  • 実際に描画された当該 text 要素の EM box(外枠) の左上の座標を (Rx, Ry)、高さを Rh


とすると、

text-before-edge 相当位置 Ry + (Ty - Ry)
central 相当位置 Ry + ( (Ty - Ry) - (Rh / 2) )
text-after-edge 相当位置 Ry + ( (Ty - Ry) - Rh )

になると思われる。


transform 属性には、

translate(<tx> [<ty>])
tx, ty による 並進 を指定する。
<ty> が与えられていない場合、 0 とみなされる。

座標系, 変換, 単位 – SVG 1.1 (第2版)

のような変換定義を指定してやればテキストの移動が可能なので、例えば central の場合には
tx = 0, ty = (Ty - Ry) - (Rh / 2)
を指定してやればよい。
tx, tyは、EM box左上端からの相対位置なので注意。


コピィ・ライターでは、具体的には、以下のようなスクリプトを SVG 内に挿入した。

var useragent = window.navigator.userAgent.toLowerCase();
if (useragent.indexOf('msie') < 0 && useragent.indexOf('trident') < 0) {
    return; // IE 以外は何もしない
}
window.onload = function() {
    var elm_text_list = document.getElementsByTagName('text');
    for (var ci=0, len = elm_text_list.length; ci < len; ci++) {
        var elm_text = elm_text_list[ci]
        ,   text_y = parseInt(elm_text.getAttribute('y'))
        ,   rect = elm_text.getBBox()
        ,   rect_y = rect.y
        ,   rect_height = rect.height
        ,   offset_y = (text_y - rect_y) - (rect_height / 2); // central の場合
        
        elm_text.setAttribute('transform', 'translate(0, ' + offset_y + ')');
    }
};


なお、IE 以外の場合には、別途 SVG の text 要素に対して dominant-baseline プロパティを設定してあるため、スクリプトでは処理を行わない。

結果

実際に上記手法を適用して作成したSVG画像が、Chrome と IE でどう見えるかを示す。
赤線は基線(SVGのテキスト要素で指定したY座標)、テキストの周囲の四角はEM box(スクリプトで描写)

Google Chrome (バージョン 43.0.2357.132 m)


Windows 7 上で確認した限りでは、Firefox や Opera の最新版でもほぼ同様に見えた。

IE11

注意点

  • SVG ファイルを HTML中に貼り付ける際、IMG要素 を使用すると、SVG内のスクリプトも実行されないため、上記の小細工が適用されない。
    これはCSSのbackground-image等で指定する場合でも同様。
  • OBJECT要素や IFRAME要素で指定してやれば、意図通りに表示される。
    ただし、IFRAMEだと扱いが比較的面倒であるため(CSSで拡大縮小しづらい等)、OBJECT要素を使用するのがベター。

コピィ・ライター(CopieWriter) - はてなコピィもどきのバナーを作成可能なサービスを作ってみた

はてなコピィのように、テキストを配置してバナーを作成でき、途中で保存/再開も可能なサービスを作ってみました。

http://furyu.nazo.cc/CopieWriter/

CopieWriter: コピィ・ライター

GitHub のリポジトリはこちら
サービスと言いつつ、ZIPをダウンロード・解凍したフォルダの index.html を開けば、ローカルでも動作します。

画面サンプル

http://furyu.nazo.cc/CopieWriter/?setting=samples/%E3%81%AF%E3%81%A6%E3%81%AA%E3%82%B3%E3%83%94%E3%82%A3%E3%82%82%E3%81%A9%E3%81%8D.json

特徴

長所
  • JavaScript だけで動作している(クライアントサイドだけの処理で、サーバーとの通信は行わない)ため、比較的さくさく動作する。
  • フレーズ数の上限は設けていない(ただし、多すぎると重くなる)。
  • 途中保存して中断(設定をダウンロード)や再開(設定の読み込み)が可能。
  • 設定ファイル(JSON形式)内の値を編集すれば、バナーの大きさやフォントも変更可能。
短所
  • サーバーサイドの処理は一切ないため、はてなコピィのような一覧ページやリンクページといったものは自動では作れない。
  • はてなコピィとのデータ上の互換性はない。
  • 同じく、操作性も異なるので、慣れるまで若干とまどうかも。
  • ブラウザによって出力結果が異なったり、うまく動作しない可能性あり。
    基本、PC版のブラウザのみ対応。IE11・Chrome・Firefox・Operaの最新版であれば多分動作する。ただしSafariでは多分うまく動作しない。

経緯

はてなコピィはテキストを自由に配置してバナーを作れるということで、お気に入りのサービスです。
最近はごぶさたしているものの、これまでも結構な数のコピィを作成しています


ですが、作成途中の保存は出来なかったりフレーズ数の制限によって、しばしば涙を飲みました。*1


以前から、いつの日かこれらを解消できたらなぁ……と思っていましたが、昨日ふと
「『テキストを配置してバナーをつくり保存する』くらいの機能なら、モダンブラウザ*2なら canvas なりを使えば、クライアントサイドのみで実現できるのではないか?」
と思い立ち、そのまま勢いで作ってみたものです。

*1:「いや、そもそもアドバンスモードでお絵かきするのは、本来の使い方じゃないんんじゃ?」という意見は聞きません

*2:そろそろ死語?

歌詞検索サービスの歌詞(テキスト)コピー禁止手法について調べてみた

動画投稿(共有)サイトやブログ等への歌詞掲載が実施しやすくなりつつある一方で、歌詞検索サービス等ではまだ歌詞(テキスト)のコピー禁止措置が取られているところが大半の模様。
このコピー禁止のための手法について、幾つかの歌詞検索サービスで調べてみた(対象はPC向けサービスのみ)。
おまけで、解除用のブックマークレットも試作してみた
その後、ユーザースクリプト化してみた



JASRAC による個人ブログ等における歌詞掲載利用許諾の概要

恥ずかしながら知らなかったのだが、個人ブログ等への歌詞掲載について、JASRAC では2012年末より、サービス運営事業者が JASRAC と許諾契約を締結することで、ユーザーが個別に許諾を得ることなく歌詞の掲載が出来るようになっている模様。

そこで、動画投稿(共有)サービスにおいてユーザーがJASRAC管理楽曲をアップロードする行為をサービス運営事業者に許諾することで動画投稿(共有)サービスにおけるJASRAC管理楽曲の適正な利用を推進している事例にならって、ユーザーが開設するブログ等でJASRAC管理楽曲の歌詞を掲載利用することにつき、ユーザーに替わってサービス運営事業者に許諾する際の条件を定めた次第です。なお今回の措置は非商用配信における歌詞掲載利用を対象としたものであり、大量の歌詞を掲載する歌詞閲覧サービスや広告料収入を得るなどの目的で行う配信利用は除かれます

ブログサービス等の運営事業者に対し、個人ブログ等における歌詞掲載利用を許諾することについて

太字は引用者修正。

JASRAC と利用許諾契約を締結しているUGC(User-Generated Contents)サービス(動画投稿(共有)サイト・ブログサイト等)のリストも公表されている。

動画投稿(共有)サイトやブログサービス等のUGC(User-Generated Contents)サービスでJASRAC管理楽曲を利用されることについて、一般ユーザーの皆さまからのお問い合わせが多いことから皆さまの利便性を考慮し、JASRACと利用許諾契約を締結しているUGCサービスのリストを公表します。


JASRACと利用許諾契約を締結している以下のサービスでは、一般ユーザーの皆さまが個別にJASRACへ利用許諾手続きを行なわなくともJASRAC管理楽曲を利用したUGC(動画・歌詞)をアップロードすることが可能です。

利用許諾契約を締結しているUGCサービスリストの公表について

つまり、アメーバやSeesaa、Yahoo!、ライブドア等のブログを使っている人は、個別に許諾を得ることなく、自分のブログに歌詞をアップロードできるということ(大量掲載・広告料収入目的等の利用は不可)。
あれ、はてな は……?はてなも2019年07月01日よりJASRAC管理楽曲の歌詞の掲載が可能となったようです


歌詞検索サービスのコピー禁止状況

とりあえず、『歌詞 検索』で検索をかけてみて、現時点で表示されるもののうち、上から5つのサービスについて調べてみた。


歌詞(テキスト)のコピー禁止手法としては、以下のようなものが使用されている模様。

  • テキスト選択無効化
  • コンテキストメニュー無効化(いわゆる右クリック禁止)
  • 歌詞部分の非テキスト化(FlashやCanvasによる表示、画像化など)

各サービス毎のコピー禁止手法一覧(2015/07/06現在)

サービス名 テキスト選択無効化 コンテキストメニュー無効化 Flash Canvas 画像 備考
歌詞検索サービス 歌ネット 画像はSVG形式
うたまっぷ 歌詞を無料で検索表示 IE11等ではFlash使用
歌詞検索サービス 歌詞GET
歌詞ナビ 無料歌詞検索サイト
歌詞検索J-Lyric.net

コピー解禁用ユーザースクリプト(Greasemonkey / Tampermonkey)[2015/07/07追記]

下記のブックマークレットを集約してユーザースクリプト化・対象サービスも追加。

【歌詞解禁(KashiKaikin)】 歌詞検索サービスの歌詞テキストコピーを可能に

Greasemonkey / Tampermonkey が入っていれば ↑ をクリックすることでインストール可。

GitHub - furyutei/KashiKaikin: 【歌詞解禁】 歌詞検索サービスの歌詞テキストコピーを可能に

コピー解禁用ブックマークレット(Hatena::Let

コピー禁止手法を調べたついでに、解除方法についても検討し、ブックマークレットを試作。
今のところ、上記のいずれのサービスでも、ユーザー側でブックマークレットを実行すれば、歌詞テキストのコピーが可能になる。

■ 集約版(2015.07.08 追記: 下の各々のブックマークレットの機能をひとつに集約したもの・ユーザースクリプト版と同等)

歌詞解禁(KashiKaikin) - Hatena::Let




※以下は旧バージョン。特に理由がないかぎり、↑の集約版の使用を推奨。

さくらのレンタルサーバ・共有SSLで .htaccess によりSSLのみのアクセス許可を設定する方法

さくらのレンタルサーバの仕様が変更になっており、この記事の内容は現状に合わなくなっている可能性が高いです。ご注意ください。
詳細は以下のページをご参照願います。
help.sakura.ad.jp
help.sakura.ad.jp
【WordPress】常時SSL化プラグインの使い方 – さくらのサポート情報



さくらのレンタルサーバで共有SSLを使用する際に、SSLのみのアクセス許可を設定(SSLアクセス強制のため、http://〜 は https://〜 へリダイレクト)するための .htaccess の記述方法を調べてみた。


設定する .htaccess の内容

[2016.07.01修正]
以下の例で、当初は

RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]」

のようにしていたが、これだと対象ディレクトリ直下で且つファイル名を指定しない(「/」で終わる)URLの場合( http://~/wordpress/ 等)にはリダイレクトされないため、修正("."→"^")。

<IfModule mod_rewrite.c>
RewriteEngine On
# --- SSLアクセスでない場合(${ENV:HTTPS} が 'on' でなく、且つ、%{HTTP:X-Sakura-Forwarded-For} が未設定の場合)には
#     https://%{SERVER_NAME}%{REQUEST_URI} へリダイレクト
RewriteCond %{ENV:HTTPS} !^on$
RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
</IfModule>


なお、WordPress を設置している場合(例として /wordpress/ 下)

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /wordpress/

# --- SSLアクセスでない場合(%{ENV:HTTPS} が 'on' でなく、且つ、%{HTTP:X-Sakura-Forwarded-For} が未設定の場合)には
#     https://%{SERVER_NAME}%{REQUEST_URI} へリダイレクト
RewriteCond %{ENV:HTTPS} !^on$
RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /wordpress/index.php [L]

</IfModule>

のようにすればよいと思われる。
WordPress の元の .htaccess の RewriteBase 行と最初のRewriteCond 行の間に、SSLアクセス強制用の RewriteCond・RewriteRule を挟み込む。 /wordpress/(二か所)は環境にあわせて書き換えること。

また、wp-config.php の「/* That's all, stop editing! Happy blogging. */(/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */)」直前に、

if ( isset($_SERVER['HTTP_X_SAKURA_FORWARDED_FOR']) ) {
    $_SERVER['HTTPS'] = 'on';
    $_ENV['HTTPS'] = 'on';
}

/* That's all, stop editing! Happy blogging. */

という記述を追加しておく。

解説(覚え書き)

さくらのレンタルサーバで共有SSLを使用する場合、以下の点に留意する必要がある。

.htaccess からの参照時
  • %{SERVER_PORT} には、SSLかそうでないかによらず '80' が設定される。
    このため、%{SERVER_PORT}ではSSL接続かどうかの判別はできない。
    「RewriteCond %{SERVER_PORT} ^80$」や「RewriteCond %{SERVER_PORT} !^443$」は常に真となるために、リダイレクトループが発生してしまう。
  • SSLアクセス時には通常、 %{ENV:HTTPS} には 'on' が、%{HTTP:X-Sakura-Forwarded-For} にはクライアント(リクエスト元)のIPアドレスが設定される。*1
    ただし、SSLアクセスした場合であっても、mod_rewrite.c の RewriteRule によりリライトされるケースでは、リライト後には %{ENV:HTTPS} が未設定となってしまう。
    RewriteRuleの[R]フラグによりhttps://〜にリダイレクトされた場合には 'on' が設定される。


上記の .htaccess では、リダイレクトするかどうかの判別に、

RewriteCond %{ENV:HTTPS} !^on$

だけでなく、

RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$

のような条件(%{HTTP:X-Sakura-Forwarded-For}が未設定)を設定している。


これをせずに %{ENV:HTTPS} のみで判定してしまうと、例えば WordPress を設置したサイト上の http://〜/wordpress/year/month/day/ というページ(URL)にアクセスした場合、

URL %{REQUEST_URI} %{ENV:HTTPS} %{HTTP:X-Sakura-Forwarded-For} mod_rewrite.cで適用されるルール 結果
1 http://〜/wordpress/year/month/day/ /wordpress/year/month/day/ (未設定) (未設定) RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L] [A] リダイレクト
2 https://〜/wordpress/year/month/day/ /wordpress/year/month/day/ 'on' RewriteRule . /wordpress/index.php [L] [B] リライト
3 https://〜/wordpress/year/month/day/ /wordpress/index.php (未設定) RewriteRule . https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L] [C] リダイレクト
4 https://〜/wordpress/index.php /wordpress/index.php 'on' RewriteRule ^index\.php$ - [L] [D](このまま)

のように、意図しないURLの PATH の書き換えが発生してしまう。
このため、[C] (3→4)のリダイレクトを発生させないように、条件を追加している。
むしろ「RewriteCond %{ENV:HTTPS} !^on$」の方は無くても現状では動作する。

PHP からの参照時
  • SSLアクセス時には、$_SERVER['HTTP_X_SAKURA_FORWARDED_FOR'] および $_ENV['HTTP_X_SAKURA_FORWARDED_FOR'] にはクライアント(リクエスト元)のIPアドレスが設定される。
  • SSLアクセスした場合であっても、mod_rewrite.c の RewriteRule によりリライトされるケースでは、$_SERVER['HTTPS'] と $_ENV['HTTPS'] が設定されない。
    RewriteRuleの[R]フラグによりhttps://〜にリダイレクトされた場合には 'on' が設定される。


WordPress 等では、SSL 接続かどうかを $_SERVER['HTTPS'] もしくは $_ENV['HTTPS'] の状態で判別している。
このため、上記のように $_SERVER['HTTP_X_SAKURA_FORWARDED_FOR'] が設定されているかどうかで SSL かどうかを判別し、SSL の場合には $_SERVER['HTTPS'] および $_ENV['HTTPS'] に 'on' を設定するような処理を追加してやる必要がある。

*1:将来的にさくら側で仕様変更が行われると、%{HTTP:X-Sakura-Forwarded-For}による判別が出来なくなる可能性はある。

Google App Engineのデプロイ時に認証エラーがでる場合の対策

久しぶりに GAE のアプリケーションをいじった後で、デプロイしようとすると、

Password for username: Use an application-specific password instead of your regular account password.
See http://www.google.com/support/accounts/bin/answer.py?answer=185833
However, now the recommended way to log in is using OAuth2. See
https://developers.google.com/appengine/docs/python/tools/uploadinganapp#Python_Password-less_login_with_OAuth2
2015-03-14 09:00:00,000 ERROR appcfg.py:2448 An error occurred processing file '': HTTP Error 401: Unauthorized. Aborting. 
Error 401: --- begin server output ---
Must authenticate first.
--- end server output ---

のようなエラーが出てしまった。


どうやら、アプリケーションパスワードを取得するか、OAuth2 認証を使用しろ、ということらしい(推奨はOAuth2)。


アプリケーションパスワードを取得して使用する場合には Google App Engine Launcher でもそのまま使えるが、OAuth2 認証の方だと Launcher で使用する方法が見つからなかった。コマンド プロンプトから appcfg.py を使用するしかないのかも。

アプリケーションパスワードを取得して使用する場合

エラーメッセージ内で指示されているSign in using App Passwords - Accounts Helpに書かれている通り、アプリ パスワードのページに行き、必要に応じてアプリケーションで使用しているGoogleアカウントでログイン後、アプリ パスワード(16文字)を生成する。
『端末を選択』の項目は使用している端末を選択または入力(『Windows パソコン』等)、『アプリを選択』はその他『(名前を入力)』を選んで適当な名前(『Google App Engine』など)を付ければよい。

表示されたアプリ パスワード(スペースは含まない16文字)は、他人に知られないように覚えるかメモしておく。
「このパスワードを覚えておく必要はないので……」とか書かれているが、覚えておく必要、あるよ……。


取得したアプリ パスワードは、これまで使っていたユーザーパスワードの代わりに、デプロイ時に指定する(emailはそのまま使用する)。

OAuth2 認証を使用する場合

エラーメッセージ内で指示されているPassword-less Login with OAuth2に従って、コマンド プロンプトより、

appcfg.py --oauth2 update myapp/

とする。
myapp/ は、自分のアプリケーションのフォルダを指定。
設定によっては、appcfg.py だけだと実行できないかもしれない。その場合は「python "C:\Program Files (x86)\Google\google_appengine\appcfg.py"」のようにフルパス指定して実行する。
すると、ブラウザで認証画面が開くので、画面の指示に従って認証すれば、デプロイすることが出来る。


いちいち認証手順を行いたくない場合、2回目以降は、

appcfg.py --oauth2_refresh_token=<token> update myapp/

のように token を指定して実行すればよい。


この token は、ユーザーフォルダ(%UserProfile%)にある .appcfg_oauth2_tokens に記述されているので、これをコピーして使う。

type %UserProfile%\.appcfg_oauth2_tokens

として、表示されたデータ(JSON形式)のうち、

...  "refresh_token": "<token>", ... 

のようになっている refresh_token を使用する。