Spelamはウェブブラウザ上で動作する標本ラベル作成ツールである。 Spelamはルールファイルを記述することにより かなり自由にレイアウトすることができる。 本ドキュメントはそのルールファイルの仕様を定義したものである。
Spelamはブラウザのレンダリング機能を利用して 標本ラベルを自動組版するシステムである。 以下のような機能を有している。
ルールファイルの簡単な例を示す。
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 |
|
※ namespaceはuuidでいいのかな?
{{field-name}}
でデータレコードの対応するフィールドが参照できる。
以下にデータファイルの例を示す。 データファイルのフォーマットはこちら。
CSVの場合はヘッダ行が必須で、 フィールド名は ルールファイルのfield nameで宣言する必要があり(9-11)、 変数によって参照することができる(24-26)。
1 2 3 |
|
これらのルールファイルとデータファイルを合わせてビルドを行えば、 以下のような2レコード分の標本ラベルが生成される。
実際とは少し違う
<style>
.card{
width: 22mm;
height: 16mm;
box-sizing: border-box;
padding: 1mm;
font-size: 4pt;
white-space: normal;
line-height: 1.5;
outline: solid 0.2pt black;
outline-offset: -0.2pt;
}
</style>
<div class="card">
Japan 12.xi.2014 J. Doe
</div>
<div class="card">
Japan 5.ii.2016 J. Smith
</div>
この結果をブラウザで確認しブラウザから印字することで ラベルを作ることができる。
データレコードの各フィールドは変数によって参照することができる。 フィールド名はルールファイルfield要素で宣言しておく。 変数の参照は{{name}}
で行う。 変数の参照は文字列の書けるコンテンツ内や属性内に置くことができる。
<source>
<field name="country" /> <!-- データファイルのフィールド宣言 -->
</srouce>
<h:div>
{{country}} <!-- 変数の参照 -->
</h:div>
変数はlet要素を使って定義することもできる。 letについては8章を参照されたい。
一部の属性は定数であること(データレコードに依存しない)が要求される。
変数名、style要素の内容、script要素の内容、rule名、ruleのスケール等
定数はconst要素で定義できる。
変数の名前は英字もしくは2バイト文字で始めることとする。
/^[A-Za-z\u0080-\uffff][A-Za-z_0-9\u0080-\uffff]*$/
変数や定数は変数名を2重ブレースで囲むことで参照できる。
{{name}}
変数の参照は属性値とコンテンツ内の両方で使うことができる。
<h:a href="{{url}}">{{name}}</h:a>
未定義の変数や定数を参照するとコンパイルエラーが発生する。
以下のようにコンテンツ内に文字列と変数参照と子要素が含まれている場合、
<hoge>abc{{v1}}def<uhyo>ghi</uhyo>jkl</hoge>
これらの要素は同じレベルとして扱われる。
"abc", {{v1}}, "def", <uhyo>ghi</uhyo>, "jkl"
以下のように書いたのと同じ
※ parseMode="program"
は空白を無視する
<hoge parseMode="program">
<text>abc</text>
<text>{{v1}}</text>
<text>def</text>
<uhyo><text>abc</text></uhyo>
<text>jkl</text>
</hoge>
これは通常あまり関係ないが、 seriesなどでは 関係してくる場合がある。
変数にはlet文を使うとhtml要素もしまうことができる。 この変数をもし属性文字列中で参照した場合には文字列に変換される。 その処理方法は、 子孫要素に渡ってコンテンツ部分だけを取り出して連結したものとなる。
1 2 |
|
{{}}
ある情報に付随する文字列があったとする。 例えば、標高の前に'alt. 'を付加したかったとする。
1 2 3 4 |
|
これだと標高データがあるときはいいが、データがなかったときに 'alt.'だけ出力されてしまう。
alt. 1000m
alt.
これをif文なしでうまく処理するためにnullという概念を導入する。 以下の例でseries要素は内容が一つでもnullなら全体をnullにするので、 'alt. 'も生成されない。
1 2 3 4 5 6 7 |
|
属性値 | 内部値(Javascriptでの値) |
---|---|
"" |
"" |
"{{}}{{}}" |
undefined |
"a{{}}" |
"a" |
listMode属性によって動作が異なる。
listMode属性、 generate属性、 list要素、 array要素、 series要素、 alternate要素。
一番外側に一つだけ書く。
1 2 3 4 5 6 7 |
|
spelamのすぐ内側に複数書ける。
例えば、 ピン刺し標本(?, pinned specimen?)、 液浸標本(vial specimen)、 スライド標本(slide specimen) などで別のルールを用意しておくと、 実行時にメニューから選択できる。
1 2 3 |
|
メニューに表示される名前。
任意の文字列。必須。定数。
昆虫の標本ラベルは通常3pt~4pt程度の小さな文字が使用される。 このような小さなサイズはブラウザで制限されていて使えないし、 仮に使えたとしても割付が汚くなる(整数で処理されているもよう)。 そこで、内容を大きく作ってから、 全体を縮小している(cssのtransform機能を使う)。 このときのスケール。
例えばscale="10"とすると、 全体を1/10に縮小するので 内側は10倍の単位30pt〜40ptの大きさで作れる。
あまり大きい値を指定するとブラウザによっては問題を生ずるかもしれない。 10ぐらいが適当?
デフォルトは1。定数。
データファイルの使用するフィールド名を列挙する。 ここで指定した名前は変数として参照可能になり、 テンプレート内の{{name}}
で埋め込むことができる。
1 2 3 4 5 |
|
未対応
現在ファイルの先頭から以下の文字を検索し 最初に見つかったものを区切り記号にしている。
"," ";" "|" tab
source要素の内側に書く。
<field name="country" default="Japan" />
フィールド名を変更したい場合に使う。 fromはデータファイルのフィールド名。 nameはルールファイルで使う名前。
1 |
|
オプション。定数。
フィールドが未定義であった場合や空の文字列''であった場合に 標準ではnullが使用される。 これを別の文字列に変換したい場合にdefault属性を使う。
1 |
|
オプション。定数。
ruleのコンテンツとして複数の部品を並べることができる。 部品にはhtml要素や文字列とspelam独自のものがある。 以下の例では、h:div, h:br, text, join, latitude, longitudeなどが 部品である。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
部品としてhtml要素が書ける。はず...
名前空間接頭辞を付加するか もしくはデフォルト名前空間をxhtmlにする必要がある。 この文書では'h'を付加している。
1 2 3 4 5 6 7 8 |
|
ruleの下にhtml要素を置いてその中に文字列を置く場合、 スタイルでwhite-space: normal;
を指定する必要がある。
※カードを複数枚横に並べられるようにwhite-space: nowrap;
を指定しているが この属性は子要素に引き継がれるため。
textboxの場合はプログラム内で設定している。
簡単なsvgはOKだった。
※ 未調査
html以外にもブラウザでレンダリング可能な要素なら書けるはずだが
MathMLはなぜかダメだった。htmltextでは大丈夫。
※ 詳しく調べてない。
テンプレートは複製されるためidは書けない。 以下のように別なIDが生成されるようにすれば可能だが、 どういう用途ががあるのかはわからない。
id="hoge-{{recordNumber}}"
名前空間を指定するれば書ける。
1 2 3 |
|
Spelamの変数が使える。
1 |
|
前段のシステムでhtmlテキストを生成できるときに使う。
1 |
|
スタイルシートを埋め込むことができる。 書き方はXHTMLと同じ。 部品を書ける場所ならどこに書いてもよい。 複数書いても良くすべて連結されたスタイルシートが生成される。
1 2 3 4 5 6 7 8 9 |
|
定数であること。
ruleで書いたように小さいサイズのフォントを扱うため 内部的に拡大して処理している。 したがってスタイルシートの値もそれに合わせて拡大する必要があるが オプションで変換できるようにした。
下の2つの例は同じスタイルを定義している。
1 2 3 4 5 6 |
|
オプション。定数。
共通属性は名前空間接頭辞をつければ 外部要素にも指定できる。
<h:div s:mode="list">
...
</h:div>
コンテンツ内の文字列の扱い。
parseMode = "text" | "program"
"text"の場合は文字列をそのまま扱う。 "program"の場合は空白を無視し空白以外の文字列はエラーとする。
デフォルトはリスト関連部品とブロック部品は"program"、 外部要素(h:div等)、テキスト部品は"text"。
オプション。定数。
HTMLの場合、コンテンツ内の文字はデータとして扱われる。 これはparseMode = 'text'に相当する。 インデントを保ちつつ空白を入れないようにしたい場合が時々あり、 その場合は以下のように少し妙な書き方をしないといけない。
1 2 3 4 |
|
parseMode="porogram"を指定すると空白は無視されるので 以下のようにすっきり書ける。
1 2 3 4 |
|
コンテンツ内のnullの扱い。
デフォルトは通常"list"にしておくか。 (array, series部品以外)
parseModeとlistModeを同時に設定する。
mode = "text" | "list" | "array" | "series"
設定される値は以下の通り。
mode | parseMode | listMode |
---|---|---|
text | text | list |
list | program | list |
array | program | array |
series | program | series |
コンテンツがnullの場合全体を生成しない。 ブロックを生成する部品で有効。
テキスト部品は?
generate = "always" | "ifNotNull" | "none"
以下の例では、{{contents}}
がnullの場合、 h:divを生成しない(nullを返す)。
1 |
|
htmlのclassと同様。 spelam要素もclass属性を別に持っており、 名前空間接頭辞は不要で単にclass="hoge"
のように書く。
有効なのは外部要素、ブロック部品、テキスト部品に限る。 text部品でclassを指定した場合はspanを生成する。
1 2 3 4 5 6 7 |
|
/^[a-z_\u0080-\uffff][a-z_0-9\-\u0080-\uffff]*
(\s+[a-z_\u0080-\uffff][a-z_0-9\-\u0080-\uffff]*)*$/i
'slm-'で始まる名前は内部で使用しているので使わないように。
デフォルト: parseMode="text", listMode="list"
通常はspanやdivのような構造を作らないが、 classかstyleが指定されたらspanを生成する。
1 2 |
|
parseMode="program"のとき、文字列を書くために使う。
1 |
|
日付の体裁を整える場合に使う。 範囲の指定に対応している。
1 2 |
|
内容の書き方はinputFormatに依存する。
入力書式を"ISO", "DMY", "MDY"のいずれかで指定する。 デフォルトは"DMY"。
基本的には自動的に処理されるが、月が数字であった場合DMYと解釈する。 年は4桁の数字であること。
以下全て2018年3月15日の書き方。
範囲の区切りはハイフン類に限る。
"DMY"と同様だが月が数字であった場合にMDYと解釈する。
ISO8601規格の一部のみ対応している。
出力フォーマット。 デフォルトは'd.rm.yyyy'。
formatValue ::= ((dayFormat sep)? (mothFormat sep)? yearFormat
dayFormat ::= 'd' | 'dd'
monthFormat ::= 'rm' | 'RM' | 'Mon' | 'MON' | 'mon' | 'm' | 'mm'
yearFormat ::= 'yyyy'
sep ::= [^a-zA-Z0-9]+
そのまま出力
format="d RM yyyy" → 15 III 2018
format="d.rm.yyyy" → 15.iii.2018
format="yyyy" → 2018
入力データの範囲記号は固定で DMY, MDYの場合 ハイフン類('-'|U+2010|U+2011)とする。 ISOの場合は規格どおり'/'とする。
出力フォーマットの範囲記号はU+2010固定としている。
オプション?
入力フィールドが開始日と終了日の2つに分かれている場合、 以下のように'-'(ISOの場合は'/')でつなげていただきたい。
1 2 3 4 5 |
|
緯度経度の体裁を整える場合に使う。
1 2 |
|
緯度、経度情報。
入力データは緯度と経度が分離していてもよいしまとまっていてもよい。 まとまっている場合入力フォーマットは自動的に判別されるが、 NSEW記号が含まれない場合は','区切りであることとし、 緯度経度の順であると仮定する。 左側が緯度、右側が経度。 北緯および東経が正の数値で、南緯と西経が負の数値。
35.795504, 138.389400
-27.378054, 152.961500
NSEW記号が含まれていればかなり適当に書ける。
35°47'45.1"N 138°23'21.9"E
138°23'21.84"E 35°47'43.81"N
35°47.23'N 138°23.56'E
N35°47.23' E138°23.56'
E138°23'21.84" N35°47'43.81"
出力フォーマット。デフォルトは'DMS'。
値
( 'DMS' | 'DM' | 'D' ) ( '.' decimalPlaces )?
decimalPlaces
::= [0-9]+
'DMS'は度分秒形式、'DM'は度分形式、'D'は度形式。 decimalPlacesは小数点以下桁数。 省略した場合、DMSの場合は0、DMの場合2、Dの場合は4とする。
以下の例では、DMS形式で小数点以下1桁まで表示される。
format="DMS.1"
度分秒の単位記号として何を使うか。 度の記号はU+00B0(°)固定。
symbols="forComputing"|"forPrinting"
"forComputing" | ° ' " |
"forPrintig" | ° ′ ″ |
文字列で与えられた学名を自動的にイタリック化する(italicize)。
1 |
|
Actias aliena (Butler, 1879) → Actias aliena (Butler, 1879)
この機能は不完全なので使用する場合は注意すること。 後述するアルゴリズム参照。 だいたいうまくいくと思うが 場合によっては手動で指定する必要があるかもしれない。
手動で指定する場合は イタリックにする部分を開始文字と終了文字で囲む。
1 |
|
データ中に開始記号が含まれていれば手動での指定となり、 なければ自動処理される。
開始記号と終了記号を空白で区切って指定する。 複数文字でもよい。 デフォルトは"/"。
italicIndicators="{ }"
italicIndicators="(( ))"
開始と終了が同じ場合は一つで良い。 以下の2つは同じ。
italicIndicators="/ /"
italicIndicators="/"
記号を印刷用のものに変換する。
symbols="" | "forPrinting"
ASCIIアポストロフィー0x27(')を Unicode 右引用符U+2019(’)に変換する。
※ 栽培種で使われる記号
引用符と考えられるASCIIアポストロフィー0x27(')を Unocodeの左右引用符 U+2018(‘), U+2019(’)に変換する
おそらくデーターベース中では 左引用符、右引用符、アポストロフィーも同じ記号(')が使われていると思う。
これを自動判別して処理するのだが うまくいくかどうか…
'の後ろに英小文字が来るのは名前として使われているため、
't Hart
以下のようなパターンで処理している。
\s\'[A-Z]
※雑種で使われる記号
ASCII小文字の'x'を乗算記号U+00d7(×)に変換する。
栽培種の処理も行っているがここでは割愛する。 (正しいのかどうかもよくわかっていない)
[1] 種小名を探す
以下のパターンであり、
\s*[a-z][a-z\-\u2010\u2011]*\s
以下のキーワードでないものを種小名とする。
'subgenus', 'section', 'series',
'x',
'ex', 'et', 'in', 'lato', 'nec', 'non', 'sensu', 'strict',
[2] 種小名より左側の文字列に対して
[2.1] 属名
先頭から始まる英大文字で始まる名前を属名としてイタリック化する。
^[A-Z][a-z\-\u2010\u2011\.]*\s
'.'は以下のようなのを処理するのに入れてある。
Dianthus caryophyllus × D. plumarius
[2.2] 亜属名
続く()で囲まれた英大文字で始まる名前を亜属名としてイタリック化する。
\(\s*[A-Z][a-z\-\u2010\u2011]*\s*\)
[2.3] その他
続く以下のキーワードの後ろの大文字で始まる名前をイタリック化する。
cf. aff. genus gen. subgenus subgen. subg. section sect. series ser.
[3] 種小名をイタリック体で出力
[4] 種小名より後ろの文字列に対して
[4.1] 亜種名
種小名のすぐ後ろに以下のパターンがあった場合、
\s*[a-z][a-z\-\u2010\u2011]*\s
以下のキーワードはイタリック化しないで出力。
'subgenus', 'section', 'series',
'x',
'ex', 'et', 'in', 'lato', 'nec', 'non', 'sensu', 'strict',
以下のパターンを人名としてイタリック化しないで出力。
'da', 'de', 'del', 'den', 'di', 'du',
'la', 'le', 'ten', 'ter', 'van', 'von', 'zur'
それ以外は亜種名としてイタリック化して出力する。
[4.2] 亜種名より後ろは、'f.'などの修飾子の後ろの 小文字で始まる名前をイタリック化する。
subsp. sub. ssp. var. forma f.
大文字化する。
<capitalize>{{country}}</capitalize>
JavascriptのtoUpperCase()を使っている。 アクセント記号付き文字も大丈夫みたい。
リスト関連部品はそれ自体DOM要素を作らないので、 classやstyle属性の指定は無視される。
parseMode: "program"
listMode: 部品による
list, array, seriesはlistModeで指定した場合も同様な効果がある。
文字列リテラルは変数参照で分離される。 以下の例は
1 |
|
以下のように書いたのと同じ。
1 2 3 4 5 |
|
コンテンツ内のnull要素は捨てられる。 コンテンツ内の全ての要素がnullなら全体がnull。 ただし要素の指定がない場合は空の列を生成する。
<list><text>a</text><text>{{}}</text><text>b</text></list>
<!-- JS:[ "a", "b" ] -->
<list><text>{{}}</text><text>{{}}</text></list>
<!-- JS:undefined -->
<list></list> <!-- JS:[] -->
<list/> <!-- JS:[] -->
parseMode = "program"
listMode = "list"
joinでデータフィールドが空のときに余計な','を生成したくない場合
1 2 3 4 5 6 7 8 9 |
|
コンテンツ内のnull要素は保存される。
<array><text>a</text><text>{{}}</text><text>b</text>
<!-- JS:[ "a", undefined, "b" ] -->
<array></array>
<!-- JS:[] -->
parseMode = "program"
listMode = "list"
以下の例では空白だったフィールドの間にも', 'が生成される。 良い例が思い浮かばないが、','が必須な場合。
1 2 3 4 5 6 7 8 9 10 |
|
コンテンツ内に一つでもnullが含まれていたら全体をnullにする。 すべての要素がnullでなければ結果はarrayやlistと同じ。
parseMode = "program" listMode = "series"
以下の例ではcollectedByがnullでなかった場合のみ、'coll. 'を付加する。
1 2 3 4 |
|
コンテンツ内の要素を順番に処理して、 最初にnull以外だったものを結果とする。 デフォルト値の指定などに使う。
parseMode = "program"
以下の例では{{country}}が空白だった場合に、'Japan'を生成する。 この例はfieldのdefaultも可能。
1 2 3 4 |
|
配列の要素を分離符で区切って連結したものを生成する。 コンテンツには2つの要素を書く。 最初の要素は分離符で2つ目の要素はリスト。 分離符はとくに制限はない(文字列でなくても良い)。 リストには通常array, list, seriesなどを使う。 listを使うと内容がnull
であった場合、分離符は生成されないが、 arrayの場合は生成される。
1 2 3 4 |
|
parseMode = "program"
listMode = "array"
例
1 2 3 4 5 6 7 8 |
|
separatorがnullの場合は、空列""として処理する。 arrayがnullの場合は、全体をnullとする。
あふれ詰め込み機能(copy fitting)を持つ枠。
※用語がよくわからない。箱組というのも関係しそうだけど、 おそらく業界によって定義が違いそう。 英語はcopy fittingで良さそう。
あふれ詰め込みは 内容が枠の大きさの範囲内に収まるように、 文字の大きさや枠の大きさを調整する機能である。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
枠の大きさと文字サイズ。 固定の場合は、スタイルで指定してもよい。 指定する場合は、minとmaxは両方を指定し単位は同じであること。 minとmaxは同じ値でもよい。 絶対単位(mm,pt等)の場合、ruleのscale属性が適用される。
2重にdivが作成される。 クラス名を指定した場合は外側のdivに設定される。
<div class="classname">
<div>{{contents}}</div>
</div>
文字は大きいほうがよく、枠は小さい方が良いとする。 一次元のパラメタ、評価値(e: 0<=e<=1)をを考え、 以下のような関数でフォントサイズと枠の大きさに対応付ける。 e=1は最も評価が良い状態で e=0は最も評価が悪い。
Fs(e) = maxFontSize*e + minFontSize*(1-e)
Wd(e) = minWidth*e + maxWidth*(1-e)
Ht(e) = minHeight*e + maxHeight*(1-e)
あるeにおけるFs(e)とWd(e)を元に 文字を割り付けた時の高さH(e)とすると、 H(e) <= Ht(e) となる最大のeを求める。
H(e)は必ずしも単調増加ではないが、大域的に単調であるので 2分法を用いて処理している。
e = 0で入らない場合はエラーとする。
主に枠を回転する目的で使う。
CSSには変形の機能があるが、 大きさが可変の枠を変形したあとの大きさ 大きさを使って割り付けることがたぶんできない。 Javascriptを使って処理している。
1 2 3 |
|
回転する角度を指定する。 単位は"deg"しか対応していない。 正の値で時計回り、負の値を指定すると反時計回りに回転する。
鏡像のために入れた。
倍率を指定してもよいが、 ruleのscale属性を反映している箇所は 自分で処理する必要がある。
2重にdivが作成される。 クラスを指定した場合はは外側のdivに設定される。
<div class="classname">
<div>{{contents}}</div>
</div>
まず内容の大きさを先に求める。 その内容に指定した変形を掛ける。 変形されたものが収まるような最小の枠をその外側に生成する。
MITライセンスのコードがあったので入れてみた。 信頼できるものなのかどうか調査したほうが良いと思うぞ。
1 2 3 4 5 6 7 |
|
コードをしまう。 エンコーディングはUTF-8固定。 スペースもそのまま入るので、無駄なスペースを入れないように注意。
qrcode規格のエラー訂正レベルを指定する。 デフォルトは'Q'。
'L' | 'M' | 'Q' | 'H'
Hが最も高い。 エラー訂正レベルは高い方が信頼性も高いが、 パターンが複雑になるため小さい面積に印字するのは難しくなる。
工事中
定数の宣言。 定数名は変数と同じように参照できる。 コンパイル時に評価され、値は常に文字列化される。 値として変数が出現した場合はコンパイルエラーになる。 また未定義の名前が出現した場合もコンパイルエラーとなる。
<const name="name">value</const>
定数名は一つ上の要素内でのみ有効。
<textbox>
<const name="hoge">...</const>
<!-- このtextbox内でのみ有効 -->
</textbox>
変数の宣言。 スコープは定数と同じ。
<let name="name">value</let>
Javascript関数の呼び出し。 scriptで定義した関数を呼び出せる。
1 |
|
任意の属性を指定できる。
関数の実体をJavascriptで定義する。
書き方の詳細はSpelam関数(工事中)を参照。
1 2 3 4 5 6 7 8 |
|