本記事は生成AIと共同で執筆しています。事実関係は可能な範囲で公式ドキュメント等と照合していますが、誤りが含まれている可能性があります。重要な判断を行う前にご自身でも一次情報をご確認ください。
IIIF(International Image Interoperability Framework)の画像ビューアを運用していて、コマ一覧のサムネイルが表示されないコレクションがある、という症状に遭遇しました。同じビューアでも、自前でホストしている画像は問題なく、外部の IIIF Image API を参照しているコレクションだけがサムネイルだけ壊れる、という切り分けにくい出方でした。
調べた範囲では、原因はサムネイル URL を組み立てるときのサイズ指定の書き方でした。full/!w,h/(先頭に ! が付く形式、sizeByConfinedWh)を使っていたのですが、これは IIIF Image API の compliance level 1 のサーバでは対応が保証されていない指定でした。日本語で明示的に整理された情報をあまり見かけなかったので、実測と合わせて記録しておきます。
サムネイル URL の形
このビューアでは、コマ(ページ)のサムネイルを次のような形で生成していました。幅 240、高さはその 2 倍を上限とする箱に収める、という意図です。
{IIIF Image API サービスのベース}/full/!240,480/0/default.jpg
サイズ部分の !240,480 は、先頭の ! が「縦横 240×480 の箱に、アスペクト比を保ったまま収まるように縮小する」という意味を持ちます。IIIF Image API の用語では sizeByConfinedWh と呼ばれる指定です。
自前のタイルサーバ(Cantaloupe)を参照している画像ではこの URL でサムネイルが返ってきていました。一方、国立国会図書館デジタルコレクション(以下 NDL)の IIIF Image API を参照しているコレクションでは、同じ形の URL でサムネイルが表示されませんでした。
info.json を見る
IIIF Image API のサーバは、各画像の info.json に自身が対応する機能を宣言しています。NDL の画像で確認したところ、次のようになっていました(一部抜粋)。
{
"@context": "http://iiif.io/api/image/2/context.json",
"protocol": "http://iiif.io/api/image",
"width": 5169,
"height": 3461,
"profile": [
"http://iiif.io/api/image/2/level1.json",
{ "formats": ["jpg"], "qualities": ["default"],
"supports": ["regionByPct", "sizeByWh"] }
]
}
読み方は次のとおりです。
profileの 1 つ目の文字列.../level1.jsonが compliance level を表します。ここでは level 1 です。profileの 2 つ目のオブジェクトのsupportsは、その level の必須機能に加えて追加で対応している機能の一覧です。ここではregionByPctとsizeByWhを追加で対応、と宣言しています。
つまりこのサーバは「level 1 の必須機能 + sizeByWh + regionByPct」に対応する、と自己申告しています。ここに sizeByConfinedWh(!w,h)は含まれていません。
実際に叩いて確認する
info.json の宣言と実際の挙動が一致するか、いくつかのサイズ指定を実際にリクエストして HTTP ステータスを見てみました。
base="https://www.dl.ndl.go.jp/api/iiif/1645378/R0007"
for sz in "!240,480" "240,480" "240," ",480" "pct:25"; do
code=$(curl -s -o /dev/null -w "%{http_code}" "$base/full/$sz/0/default.jpg")
echo "$sz -> $code"
done
結果は次のとおりでした。
| サイズ指定 | IIIF での名前 | 意味 | 応答 |
|---|---|---|---|
!240,480 | sizeByConfinedWh | 箱に収める(比率維持) | 500 |
240,480 | sizeByWh | 幅・高さを指定(比率は問わない) | 200 |
240, | sizeByW | 幅のみ指定(比率維持) | 200 |
,480 | sizeByH | 高さのみ指定(比率維持) | 200 |
pct:25 | sizeByPct | パーセント指定 | 200 |
info.json の宣言どおり、!240,480(sizeByConfinedWh)だけが失敗しています。ステータスは 500 でした(400 ではなく 500 が返ってくるのはサーバ側の実装の都合と思われますが、いずれにせよ画像は得られません)。ブラウザから見ると、この URL を img の src にしていたサムネイルだけが壊れて見える、という症状になっていました。
compliance level と size 指定の対応関係
IIIF Image API には compliance level(level 0 / 1 / 2)という段階があり、どのサイズ指定に対応する義務があるかが level ごとに決まっています。IIIF Image API 2.1 の compliance ドキュメントを確認したところ、サイズ指定については次の対応でした。
sizeByW(w,)、sizeByH(,h)、sizeByPct(pct:n)は level 1 で対応必須です。level 1 以上のサーバなら必ず使えます。sizeByWh(w,h)とsizeByConfinedWh(!w,h)は level 2 で対応必須です。level 1 では任意で、対応するかどうかはサーバ次第です。対応する場合はsupportsに列挙されます。
なお level 0 は full(原寸)しか返さず縮小指定を持たないため、縮小サムネイルを作る時点で level 1 以上が前提になります。また本記事は Image API 2.x を対象にしています。調べた限りでは 3.0 では compliance 表が一部変わり、たとえば sizeByPct は level 1 では任意になるようなので、3.x のサーバを相手にする場合は改めて確認したほうがよさそうです。
NDL のサーバは level 1 で、supports に sizeByWh は入れているものの sizeByConfinedWh は入れていません。したがって !w,h は対象外、という整理になります。ここで重要なのは、これは NDL に固有の話ではなく、level 1 のサーバであれば !w,h に対応していないことは仕様上あり得る、という点です。自分でホストしていない外部の IIIF Image API を参照する場合、level 1 のサーバに当たる可能性は十分にあります。
なお w,h(sizeByWh)は「幅と高さをそのまま指定する」指定で、指定した縦横比が元画像(あるいは切り出し領域)の比率と一致しない場合は画像が歪みます。サムネイルの箱にきれいに収めたいという用途では、比率を保ってくれる !w,h を使いたくなるのですが、その !w,h が今回の落とし穴でした。
幅指定(sizeByW)に切り替える
対処としては、サムネイルのサイズ指定を !w,h(sizeByConfinedWh)から幅のみの w,(sizeByW)に変えました。
{サービスのベース}/full/240,/0/default.jpg
sizeByW は level 1 で対応必須なので、level 1 以上のサーバならどこでも通ります。比率も維持されます。違いは高さの上限が URL 側では効かなくなることで、サムネイルの表示枠は元々 CSS 側(w-full などの幅指定と、必要なら max-height)で制御しているため、見た目は実質変わりませんでした。ただし表示だけでなく転送量の観点もあり、w, は高さを拘束しないので、極端に縦長の画像(巻子など)では想定より大きな画像が返ってきます。その場合は逆に高さのみの ,h(sizeByH、これも level 1 必須)を使う手もありますが、,h は今度は幅を拘束しないので、極端に横長の画像では同じことが起きます。扱う資料の縦横比に合わせて幅か高さのどちらかを選ぶ、というのが実際のところで、いずれにせよ一方だけを指定する形が対応サーバの範囲は最も広くなります。
自前のタイルサーバ(Cantaloupe は level 2)を参照している箇所では !w,h でも問題なく動いていましたが、外部サービスを参照しうる箇所(マニフェスト内の thumbnail、コマ一覧、類似画像パネルなど)はまとめて w, に寄せました。外部の IIIF Image API をプロキシ・フェデレーションして自サイトのビューアに載せる構成では、参照先の compliance level を自分で制御できないため、サムネイルのサイズ指定は level 1 の必須機能だけで組み立てておくのが無難だと感じています。
整理
今回の切り分けから得られた要点は次のとおりです。
- サムネイルなど「サイズを縮めて取得する」URL を組み立てるときは、参照先の
info.jsonのprofile(level)とsupportsを確認する。 !w,h(sizeByConfinedWh)とw,h(sizeByWh)は level 2 で必須・level 1 では任意。level 1 のサーバでは対応していないことがある。- 対応サーバの範囲を最も広く取りたいなら、幅のみ
w,(sizeByW)または高さのみ,h(sizeByH)を使う。これらは level 1 で対応必須。 - 縦横両方を箱で制約したい場合は、URL で無理に縮めず、表示側の CSS で枠を制御する。
自分でホストしている画像だけを扱っているうちは level 2 前提で !w,h を使っていても気づきませんが、外部の IIIF Image API を混ぜた瞬間に level の差が表面化します。IIIF の相互運用性は「level 1 以上のどのサーバでも通る指定を選ぶ」ことで成り立っている、という当たり前の点を、サムネイル 1 枚から再確認した形でした。



コメント
…