テクデップ(Techdep)

コンピュータ、プログラミング、DTP(InDesign)に関する備忘録

InDesignでのダブルミニュートと全角ダーシの禁則処理

本記事を要約すると、縦書き用の引用符であるダブルミニュート(U+301D、U+301F)と全角ダーシ(U+2015)を禁則対象文字に登録しておくべし。

禁則処理と表示グリフ

InDesignでは実際に表示されるグリフ(CIDに基づいた)は、組方向、字形タグなどによって決定される。場合によっては、異なるUnicodeでありながら、表示されるグリフは同一になることもある。このような文字として引用符や全角ダーシがある。

さて、InDesignはある文字が禁則対象であるかどうかはUnicodeのほうで判定する。ここで問題なるのがそれのデフォルト設定である。InDesignの「弱い禁則」には“”(U+201C、U+201D)は登録されているが、〝〟(U+301D、U+301F)が登録されていない。また、強弱どちらの禁則にもダーシ(U+2014)があるが、全角ダーシ(U+2015)はない。

つまるところ、同じグリフなのに異なるUnicodeであるため、禁則処理が異なる結果がありうる。引用符の混在あるいは全角ダーシが混在している原稿であると、下記の様に禁則処理結果が不適当なものになる。一方で禁則処理が全くなされていないのは、この文字が禁則文字として登録されていないためだ。

f:id:seizo_igawa:20160213211122p:plain

引用符の場合

横書き用の引用符(“”)とはInDesign上で等幅全角字形を適用すると、縦書き用の引用符(〝〟)に置換される。表示上では同じグリフなので、“”(U+201C、U+201D)か〝〟(U+301D、U+301F)であるかの区別はつかない。情報パネルからは異なるUnicodeポイントであることはわかる。

Unicode CID 備考
U+201C 108 -
U+201C 7956 等幅全角字形の適用時
U+301D 7956 -
U+201D 122 -
U+201D 7957 等幅全角字形の適用時
U+301F 7957 -

全角ダーシの場合

似た様なことが全角ダーシ(―、U+2015)にも言える。U+2015であると縦組みか横組みかで呼び出されるCIDが異なる。

Unicode CID 備考
U+2014 138 -
U+2014 661 等幅全角字形の適用時
U+2015 661 横組み
U+2015 7892 縦組み、字形パネルで見るとU+FE31のグリフが呼び出される模様

対応方法

記号類が混在した原稿もありうるし、Windows環境で作成された原稿は基本的には〝〟(U+301D、U+301F)や―(U+2014)で入力されることが多い。そうなると引用符や全角ダーシの禁則処理が不適切な組版が世に出ることになる。何らかの対応をしないといけない。

対応方法としては色々あるけれども、禁則処理テーブルに引用符(U+301D、U+301F)と全角ダーシ(U+2015)とを登録しておこう。全角ダーシ(U+2015)は「強い禁則」、「弱い禁則」ともに登録されていないので登録しておく。引用符は「弱い禁則」に登録しておく。

Python向けのヘッドレステスト環境の構築

Django等でheadless test(GUIブラウザを利用しないウェブアプリケーションのテスト)を実行するための環境構築の覚書。尚、筆者の環境は以下の様になって居る。

準備

seleniumの導入

pip install selenium

PhantomJSの導入

このサイト(http://phantomjs.org/)から導入する。導入後、パスを通しておく。

動作確認

seleniumの動作確認

下記の様なコードを書いて、適当な名前で保存しよう。このコードではFirefoxを起動させて、Python公式ページで検索して、Firefoxを終了させるまでを記述して居る。

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

# ブラウザの指定
driver = webdriver.Firefox()
driver.get("http://www.python.org")
assert "Python" in driver.title

# テキスト欄への入力
elem = driver.find_element_by_name("q")
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
assert "No results found." not in driver.page_source

# ブラウザの終了
driver.close()

実行方法は次の通り。Firefoxが勝手に起動して、自動で検索をしてくれるはず。

python selenium-test.py

PhantomJSの動作確認

下の様なJavaScriptコードを書いて、“test.js”として保存しよう。

console.log("2 + 2 = ?");
phantom.exit();

コマンドライン上で動作確認する。“2 + 2 = ?”と表示されれば問題ない。

phantomjs test.js

テストコード化

動作確認が済んだらそれぞれを組合せて、テストコードを記述していく。今回はPythonのunittestを使用する。

import unittest
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

class TestPage(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.PhantomJS()     

    def test_search_in_python_org(self):
        self.driver.get("http://www.python.org")
        assert "Python" in driver.title

        # テキスト欄への入力
        elem = self.driver.find_element_by_name("q")
        elem.send_keys("pycon")
        elem.send_keys(Keys.RETURN)
        assert "No results found." not in driver.page_source

    def tearDown(self):
        self.driver.close()

Django等のウェブフレームワークでの利用

  • 接続先を“localhost”にして試験内容を記述する。
  • Djangoのローカルサーバを立ち上げて、テストコードを実行する

【失敗事例】恐竜テーマパークにおけるインドミナス・レックスの脱走

映画ジュラシック・ワールドを観て、失敗事例報告書を思いつきました。感想代りに書いておきます(内容はちゃんと面白かったのでご安心ください)。

失敗事例:インドミナス・レックスの脱走

事例名称

インドミナス・レックスの脱走

代表図

省略

事例発生日付

2015年8月5日

事例発生地

コスタリカ イスラ・ヌブラル島

事例発生場所

ジュラシックワールド インドミナス・レックス飼育エリア

事例概要

飼育中の新種恐竜インドミナス・レックスが脱走し、飼育員2名を殺害した。また、捕獲のための部隊も全滅させられた。

事象

飼育中のインドミナス・レックスが防壁(コンクリート製)をよじ登って脱走したのではないかと勘違いした職員が、調査のために防壁内に入った。しかし、恐竜は熱カモフラージュができる性質を備えており、そのために熱源カメラに映っていないだけであり、実際にはまだ防壁内部に居た。管理センターは恐竜が防壁内部に居ることを現場に連絡したが、その直後に恐竜が職員3名に襲い掛かった。この段階で1人が死亡し、2人は防壁からの脱出を果たした。ただ、その脱出には恐竜用ゲートを利用したため、ゲートが完全に閉まりきる前に恐竜がゲートを破壊して脱走。その後、恐竜は職員1人を殺害し、島内の密林へと逃げ込んだ。

経過

事故当日、飼育エリアの防壁の安全性を確認するために調教員が訪れた。彼は本来ヴェロキラプトルの調教員であるが、その経験と知識を買われて運営責任者から直々に当該施設の安全性の確認を命ぜられて、ここを訪れたのであった。

そこで調教師はインドミナス・レックスの姿が見えないことを疑問に思い、普段インドミナス・レックスを監視している飼育員に熱源カメラでの確認を依頼した。

しかし、熱源カメラにはその姿が映らなかった。また、ここで調教師はインドミナス・レックスが内部の壁をよじ登ったと思しき真新しい爪痕を発見した。ここで居合わせた全員がインドミンス・レックスは壁をよじ登って飼育エリアから脱走したと勘違いした。そして、調教師と2人の飼育員は調査のために、無断で防壁内部に立ち入った。また、護衛用の武器を所持せずの立ち入りであった。

現場から連絡を受けた管理センターはGPSによる追跡をしたところ、恐竜は防壁内部に居ることが判明。恐竜には熱カモフラージュの性質があり、単にそれを利用して、熱カメラに映って居ないだけであった。

内部にまだ恐竜が居るとの連絡を受けた三人であるが、それと同時に姿を現した恐竜と遭遇。このときに飼育員一人が恐竜によって殺害された。

生き残った調教師と飼育員は脱出を開始。しかし、このとき脱出に使ったのは恐竜用の巨大ゲートであった。二人は脱出には成功したが、巨大ゲートであるため閉鎖に時間が掛かる仕様であり、完全に閉まりきる前に恐竜の突撃を受け、ゲートは破壊された。こうして凶暴な肉食恐竜が脱走するという事態が発生したのであった。

飼育員は車の陰、調教師は車の下にそれぞれ隠れたが、恐竜は飼育員を殺害した。調教師はガソリンを被って体臭を消すという方法で恐竜に発見されず難を逃れた。そして、恐竜は森の中へと逃げ込んだ。

その後、当テーマパークの捕獲隊が直ちに出動したが、その捕獲隊も全滅した。

原因

重大情報の横展開の欠如

飼育中のインドミナス・レックスは遺伝子組み換えによる全く新種の恐竜である。この恐竜は、熱カモフラージュするという恐るべき特性を持ち合わせており、熱源カメラに姿が必ずしも映らない。開発部門はその性質を把握していた可能性があったが、その情報は飼育部門に展開されていなかった。このことが現場での誤判断や対処の誤りを招いた一因となった。

確認手段の不備

上述の恐竜は熱源カメラに探知されない性質を持っているが、当該恐竜に対して所在を確認するために従前通りの熱源カメラを用いていたのは不適当である。これも上記の横展開不備により、飼育部門が飼育対象の性質をよく把握しないままに従来通りの監視手段を採用させてしまった。

情報確認の不徹底

恐竜の監視は熱源カメラ(現場からも確認可能)とGPSの二通りで行えたが、GPS情報は管理センターに問合せねばならなかった。しかし、現場の職員は熱源カメラの情報のみで恐竜が脱走したと判断。管理センターへのGPS情報の問合せを怠って、最終確認のために防壁内部に独断で入ってしまった。

恐竜が脱走したかもしれないという状況下において、素早い対処と判断をしたいがために、手間暇が掛かる情報の確認が省略されたことが、結果として今回の事態を招いた。

脱走防止対策の不備

ゲートは多重化がされていなかった。このため、一度の突破が被害の際限なき拡大を招いてしまった。これを防止するためにもゲートを二重化するべきであった。

防壁の強化工事は行っていたが、同時にゲートの多重化や強化はできたはずである。

対処

その後も捕獲部隊が波状的に出動し、事態の収拾に当たった。

対策

  • 飼育対象の性質は関係部門で共有する。特に開発部門と飼育部門は意思疎通に齟齬が生じやすいので、管理部門はその齟齬が発生しない様に尽力すべきである。特に新種の恐竜はそれを徹底すること。現場レベルでは、飼育する恐竜ごとに管理に関る性質のマニュアルを常備しておくこと。
  • 恐竜の所在情報は飼育員にとっては大変重要な情報である。タブレットなどで容易に確認できるようにすることが望ましい。
  • 情報の確認手順は明文化する。
  • 飼育対象の性質に応じた監視手段を用意する。
  • 肉食恐竜の防壁内部には無断で入らないようにする。また、立ち入るときは武器の携行を義務付ける。

知識化

  • 安全に関る全ての情報(今回の場合は恐竜の所在)は現場からも容易かつ即座に確認できるようにする。
  • 情報の確認手順はマニュアル化する。
  • 飼育対象の重大な性質は全ての関係部門に展開させること。
  • 危険な場所へは無防備かつ無断で入ってはならない。
  • 安全機構は多重化すること。

背景

当テーマパークはマンネリ化を防ぐために、新規アトラクションや新種の恐竜の導入を定期的に実施しており、今回のインドミナス・レックスもその一つである。この対策が功を奏して収益も右肩上がりであったが、経費も比例して増大することも経営側には頭痛の種であった。このため安全管理に対する資金投下が抑制的になりつつあり、抜本的な安全対策を取って居なかったことが、今回の事故を引き起こす土壌になっていたことは否めない。

後日談

この脱走がきっかけとなって、当テーマパークが崩壊することになったが、詳細は劇場にて確認されたい。

シナリオ

主シナリオ

組織運営不良、不注意、手順の不遵守、誤判断、調査・検討の不足

死傷者

死者数 2名(飼育員)
10名以上(第一次捕獲部隊)
負傷者数 不明

物語の構造が剥き出しの「マッドマックス 怒りのデスロード」

ネタバレ注意

友人達と「マッドマックス 怒りのデスロード」を鑑賞しました。その界隈で良い意味であれな感想しか飛び交って居ませんが、マッドマックスは物語の構造に実に忠実でしたので、今までのエントリの例に倣って、物語論から解釈しましょう。

続きを読む

2 + "2" = ?

動的型付けの言語では、数値と文字列とを足そうとしたときの挙動は言語毎に異なる。今回、PerlPythonJavaScriptにおける文字列と数値とを足したときの挙動を比較した。

Perl

#!/usr/bin/perl
print "2" + "2";
print 2 + "2";

結果は次の通り。文字列同士の演算を数値同士の演算と見なす柔軟性を発揮した。

4
4

Python

print("2" + "2")
print(2 + "2")

数値と文字列との演算はできないとインタプリンタが拒絶する結果に。

22
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    print(2 + "2")
TypeError: unsupported operand type(s) for +: 'int' and 'str'

JavaScript

<script>
console.log("2" + "2");
console.log(2 + "2");
</script>

文字列の結合演算となった。

"22"
"22"

まとめ

各言語の設計思想?がよく現れている気がする。Rubyの実行環境はないので今回は省略。

Djangoで外部キーを設定したときのフィールド表示

admin画面での外部キーの表示

下記の様に外部キーを設定して、admin.pyに各モデルを登録して、Djangoのadmin画面でモデルを管理するときの外部キーの表示方法について。Orderモデルのuser_idキー(外部キー)の表示は、参照元たるPaymentUserのstr()に依存することに注意。このstr()がないと、「PaymentUser Object」の様に表示され、どの様な値が入って居るのか全く判らなくなる。

class PaymentUser(models.Model) :
    user_id = models.IntegerField(primary_key=True)
    username =models.CharField(max_length=100)
    
    def __str__(self):
        return u'%s' % (self.user_id)
    
class Order(models.Model) :
    order_id=models.IntegerField(primary_key=True)
    user_id=models.ForeignKey(PaymentUser)
    amount=models.IntegerField() 

以下にstr()の有無で表示がどう変化するか比較画像をあげた。

f:id:seizo_igawa:20150625225222j:plain

str()が定義されて居ないと、オブジェクト名だけの表示となる

f:id:seizo_igawa:20150625225247j:plain

str()が定義された場合の外部キーの表示

参照元

The Django admin site | Django documentation | Django

上記リンク先に「A few special cases to note about list_display:」なる但し書きがあって、

If the field is a ForeignKey, Django will display the str() (unicode() on Python 2) of the related object.

とある。

ファイルバッファの強制フラッシュ(Python)

ファイルの書き出し処理といっても直ちにディスクにそのファイルが書き出されるわけではない。ディスクへの書き出しが遅れうるという挙動を失念して居て、以下のコードで意図通りの動作にならず困ったことがあった。

with codecs.open('filename', 'w', 'UTF-8') as file :
	file.write(body)

subprocess.call('cmd filename', shell=False)

「あるファイルを事前に書き出して、事前出力したファイルを入力として外部プログラムが諸々の処理をする」という単純なコードだ。しかしながら、場合によっては、subprocess.call()の時点で入力となるべきファイルが空ファイルの可能性もあり、意図通りの動作にならないのである。

ディスクへの書き出しは直ちに実行されない

ファイルの書き出し処理とはいっても、Pythonインタプリンタはデータをファイルバッファに渡したら次の処理に進んでしまう。ファイルのclose処理がされた時点でもディスク上に実体がある保証はない。ファイルの中身が依然としてバッファに格納されたままで、ディスク上は空ファイルのままのこともある。このバッファがいつ書き出されるかはインタプリンタの制御下にあり、最遅としてプロセス終了時に漸くディスクへの書き出しが始まることもある。

しかし、後続の処理がそのファイルを用いなければならないときは、ディスク上にファイルがしっかりと書き出されていないと非常に困る訣である。

flush()メソッドの登場

以下の様に一行だけ追加すれば上述の事態は避けられる。flsuh()によりバッファ上の内容は直ちにディスクに書き出されるので、後続の処理で問題が起きることはない。

with codecs.open('filename', 'w', 'UTF-8') as file :
	file.write(body)
	file.flush()

subprocess.call('cmd filename', shell=False)

参考リンク

同様の事例は標準出力でも起きうるので、sys.stdout.flush()というメソッドが用意されて居る。

また、上記事例はPythonに限らず、他のプログラミング言語でも起きる。