本記事は生成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 の必須機能に加えて追加で対応している機能の一覧です。ここでは regionByPctsizeByWh を追加で対応、と宣言しています。

つまりこのサーバは「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,480sizeByConfinedWh箱に収める(比率維持)500
240,480sizeByWh幅・高さを指定(比率は問わない)200
240,sizeByW幅のみ指定(比率維持)200
,480sizeByH高さのみ指定(比率維持)200
pct:25sizeByPctパーセント指定200

info.json の宣言どおり、!240,480(sizeByConfinedWh)だけが失敗しています。ステータスは 500 でした(400 ではなく 500 が返ってくるのはサーバ側の実装の都合と思われますが、いずれにせよ画像は得られません)。ブラウザから見ると、この URL を imgsrc にしていたサムネイルだけが壊れて見える、という症状になっていました。

compliance level と size 指定の対応関係

IIIF Image API には compliance level(level 0 / 1 / 2)という段階があり、どのサイズ指定に対応する義務があるかが level ごとに決まっています。IIIF Image API 2.1 の compliance ドキュメントを確認したところ、サイズ指定については次の対応でした。

IIIF Image API の size 指定を level 1 サーバ (NDL) に対して実行したときの応答を並べた図。上から full/!240,480 (sizeByConfinedWh) は HTTP 500 で失敗、full/240,480 (sizeByWh) は 200、full/240, (sizeByW) は 200、full/,480 (sizeByH) は 200、full/pct:25 (sizeByPct) は 200。sizeByConfinedWh は level 2 で必須だが level 1 では任意で NDL は未対応、sizeByWh は level 2 で必須だが NDL は supports に明示、sizeByW・sizeByH・sizeByPct は level 1 で必須、という注記が付いている。

  • sizeByWw,)、sizeByH,h)、sizeByPctpct:n)は level 1 で対応必須です。level 1 以上のサーバなら必ず使えます。
  • sizeByWhw,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 で、supportssizeByWh は入れているものの 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.jsonprofile(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 枚から再確認した形でした。