本記事は生成AIと共同で執筆しています。事実関係は可能な範囲で公式ドキュメント等と照合していますが、誤りが含まれている可能性があります。重要な判断を行う前にご自身でも一次情報をご確認ください。
TL;DR
- IIIF Presentation 3 のアーカイブで OCR テキストをアノテーションとして配信したら、Annona など他の viewer では出るのに Mirador 4.0.0 (
projectmirador.org/embed) の Annotations タブだけ空、という現象に遭遇した - 原因は Mirador 4.0.0 リリース版 の
config.annotations.filteredMotivationsのデフォルト値が['oa:commenting', 'oa:tagging', 'sc:painting', 'commenting', 'tagging']で、supplementingを許可していないため - Mirador master ブランチでは
filteredMotivations: [](フィルタ無し) に変更されているが、本記事執筆時点でリリースされていない。リリース版では引き続き上記フィルタが効く motivationを["commenting", "supplementing"]配列にすれば、commentingで Mirador 4.0.0 のフィルタを通過しつつ、supplementingも維持して spec 準拠 viewer や text-overlay プラグインから transcription として認識される- IIIF Presentation 3 仕様 §3.5「Values for motivation」が定義するのは
paintingとsupplementingのみ。他の値 (commenting/tagging等) は Web Annotation Data Model 由来。配列形式の許容根拠は IIIF §4.3 (Properties with Multiple Values) と Web Annotation §3.3.5 paintingを一緒に指定するのは避ける。各 canvas には既に画像のpaintingannotation があり、同じ canvas に複数の painting annotation が並ぶと viewer の挙動が実装依存になる
何が起きたか
平賀譲デジタルアーカイブ の API 拡張作業で、コマごとの OCR テキストを IIIF Annotation として配信するエンドポイントを足しました。マニフェストの canvas に annotations 参照を入れて、別 URL で AnnotationPage を返す Presentation 3 の標準構成です。
// /api/iiif/{id}/manifest.json (抜粋)
{
"items": [
{
"id": ".../canvas/p1",
"type": "Canvas",
"items": [ /* 画像の painting annotation */ ],
"annotations": [
{ "id": ".../canvas/p1/annotations", "type": "AnnotationPage" }
]
}
]
}
// /api/iiif/{id}/canvas/p1/annotations (修正前)
{
"@context": "http://iiif.io/api/presentation/3/context.json",
"id": ".../canvas/p1/annotations",
"type": "AnnotationPage",
"items": [
{
"id": ".../canvas/p1/annotations/anno-1",
"type": "Annotation",
"motivation": "supplementing",
"body": {
"type": "TextualBody",
"language": "ja",
"format": "text/plain",
"value": "製造費ノ減少ニ就テ"
},
"target": ".../canvas/p1#xywh=4875,1610,88,1282"
}
]
}
motivation には IIIF Cookbook の Recipe 0068 (Newspaper) や 0231 (Transcripts/Captions メタレシピ) が transcription / OCR で推奨する supplementing を採用しています。Annona など spec に忠実な viewer ではこれで正しくキャンバスに重ね描画されます。
ところが Mirador 4.0.0 (https://projectmirador.org/embed/?iiif-content=...) の Annotations タブを開いても項目が表示されない。manifest を読み込めて、画像も検索 (IIIF Content Search) も動いているのに、Annotations だけが空でした。
ちなみに IIIF Cookbook 0068 自身の公式マニフェストを Mirador 4.0.0 embed にロードしても、同じく Annotations タブは空になります(試せます: collection / issue manifest 直)。仕様準拠の標準例ですら出ないので、ライブラリ側の挙動として固定的なものだと当たりが付きます。
原因の特定 — 配信されている Mirador bundle を読む
最初は「Mirador 4 が motivation supplementing を panel に出さないのは仕様上 / コード上どうなっているのか」を master ブランチのソース (src/config/settings.js) で調べたところ、
annotations: {
htmlSanitizationRuleSet: 'iiif',
// filteredMotivations: if empty, all annotation motivations will be shown.
filteredMotivations: [],
},
と、フィルタなし(空配列) がデフォルトに見えました。ところが実観察と矛盾するため、実際に projectmirador.org/embed から配信されている bundle を直接読み解きます。
$ curl -sS https://projectmirador.org/embed/ | grep -oE 'src="[^"]+\.js"'
src="/assets/embed-BxGPuA7M.js"
$ curl -sS https://projectmirador.org/assets/embed-BxGPuA7M.js | grep -oE 'mirador[^"]{0,40}\.js'
mirador.es-Bh8H5vm3.js
$ curl -sS https://projectmirador.org/assets/mirador.es-Bh8H5vm3.js \
| grep -oE 'filteredMotivations:\[[^]]+\]'
filteredMotivations:["oa:commenting","oa:tagging","sc:painting","commenting","tagging"]
実際にデプロイされている bundle には、明示的に filteredMotivations が設定されています。supplementing は含まれません。
リリースタグごとの定義を遡ると:
mirador@v4.0.0 src/config/settings.js
filteredMotivations: ['oa:commenting','oa:tagging','sc:painting','commenting','tagging']
mirador@master (未リリース) src/config/settings.js
filteredMotivations: []
つまり、Mirador 4.0.0 リリースには supplementing を panel に出さない デフォルトが既定として入っていて、master では「全て出す」に変更されているけれど、まだリリースされていない、というのが現状です。projectmirador.org/embed もリリースバンドルを使っているので、フィルタが効きます。
セレクタ実装は src/state/selectors/annotations.js 由来 (bundle 内のミニファイ後コードでも該当ロジックが確認できる):
const getMotivations = createSelector(
[getConfig, (state, { motivations }) => motivations],
(config, motivations) => motivations || config.annotations.filteredMotivations,
);
export const getAnnotationResourcesByMotivationForCanvas = createSelector(
[getPresentAnnotationsCanvas, getMotivations],
(annotations, motivations) => {
const resources = flatten(annotations.map(a => a.resources));
if (!motivations || motivations.length === 0) return resources;
return resources.filter(r => r.motivations.some(m => motivations.includes(m)));
},
);
Mirador 4.0.0 では filteredMotivations が空配列でないため、r.motivations.some(m => motivations.includes(m)) の判定が効き、supplementing だけのアノテーションは振り落とされます。
解 — motivation を配列にする
motivation は IIIF/Web Annotation 仕様で複数値を取れるので、両方つけます。Mirador 4.0.0 の filter を commenting で通過させ、spec 準拠 viewer や text-overlay プラグインのために supplementing も残す。
{
"motivation": ["commenting", "supplementing"],
"body": {
"type": "TextualBody",
"language": "ja",
"format": "text/plain",
"value": "製造費ノ減少ニ就テ"
},
"target": ".../canvas/p1#xywh=4875,1610,88,1282"
}
実装側の変更は文字列リテラルを配列リテラルに直すだけです。TypeScript の場合は型注釈を 'supplementing' から tuple 型 ['commenting', 'supplementing'] に変えるのを忘れないように。
これで Mirador 4.0.0 panel に項目が出るようになります。
仕様上の根拠 — どこに書いてあるか
motivation の値域と複数値許容について、IIIF Presentation 3 仕様自体は限定的にしか書いていないので、Web Annotation Data Model を参照する形になります。整理すると:
- IIIF Presentation 3 §3.5「Values for motivation」 が定義するのは
paintingとsupplementingの 2 値のみ。同セクションの第 3 段落で「他の motivation 値は Web Annotation Data Model に従って使用してよい」(SHOULD be used) と委ねている。 - IIIF §4.3「Properties with Multiple Values」: 複数値を取るプロパティは値が 1 つでも配列にする必要がある。
- Web Annotation Data Model §3.3.5 「Motivation and Purpose」: 「There SHOULD be exactly 1
motivationfor each Annotation, and MAY be 0 or more than 1.」つまり 0 個・1 個・複数個いずれも許容。
なお commenting / tagging / bookmarking 等の motivation 値は IIIF spec ではなく Web Annotation Data Model §3.3.5 で定義されている語彙です。Mirador 4.0.0 のデフォルト filter リスト ['oa:commenting','oa:tagging','sc:painting','commenting','tagging'] のうち oa: / sc: 接頭辞ありは古い (open annotation / shared canvas) 名前空間で、prefix なしの commenting / tagging が Web Annotation 1.0 系の現行表記です。
painting を一緒に指定したくならないか
「同じ場所にテキストを描いてほしいだけなら painting motivation で TextualBody を渡せば良いのでは?」と最初は思いますが、これは避けた方が良い設計です。
IIIF Presentation 3 の各 canvas は既に画像を painting annotation として持っています:
{
"id": ".../canvas/p1",
"type": "Canvas",
"items": [
{
"type": "AnnotationPage",
"items": [
{
"type": "Annotation",
"motivation": "painting",
"body": { "type": "Image", "id": ".../full/full/0/default.jpg", ... },
"target": ".../canvas/p1"
}
]
}
]
}
ここに OCR テキストの painting を追加すると、同じ canvas を target にする painting annotation が複数並ぶ状態になります。仕様上は許容されますが、viewer がどう描画するかは実装依存で、
- 後勝ちで画像が消えてテキストだけになる
- テキスト側を空ボックスとして描画して画像を覆う
- どちらも描画するが重なって読めない
など、安定しません。OCR テキストは画像を置き換えるものではなく補足するものなので、supplementing 系の motivation で扱うのが意図に合っています。Mirador 4.0.0 panel 対応のために便宜上 commenting を併記する、というのが今回の落とし所でした。
まとめ — Mirador (4.0.0) 互換チェックリスト
OCR テキストを IIIF Annotation として Mirador 4.0.0 で表示させたい場合:
motivationは["commenting", "supplementing"]の配列で出す- Mirador 4.0.0 のリリース版では
filteredMotivationsのデフォルトがsupplementingを含まないため、単独だと panel に出ない - master では既に
[]に変更されているので、新版がリリースされたら片方でも構わない(が、後方互換のため両方入れておくのが堅い)
- Mirador 4.0.0 のリリース版では
paintingは足さない(画像の painting annotation と衝突する)- annotation の
targetは manifest 内の canvas のidと一致 +#xywh=で領域指定 - coords の単位は manifest 内 canvas の
width/heightと同じピクセル空間- OCR 元画像が IIIF source より小さい場合、xywh をスケールアップする必要がある
仕様 (
supplementing推奨) と実装 (Mirador 4.0.0 のフィルタ) のずれによる罠なので、最初から配列形式で出しておくのが無難です。Mirador 4 の今後のリリースでフィルタデフォルトが緩くなれば、この workaround は不要になる見込みです。
- OCR 元画像が IIIF source より小さい場合、xywh をスケールアップする必要がある
仕様 (
参考
- IIIF Presentation 3.0 — §3.5 Values for motivation
- IIIF Presentation 3.0 — §4.3 Properties with Multiple Values
- Web Annotation Data Model — §3.3.5 Motivation and Purpose
- IIIF Cookbook 0068 — Basic Newspaper (
supplementingで OCR を target) - IIIF Cookbook 0231 — Transcripts, Captions, and Subtitles (meta-recipe)
- IIIF Cookbook 0219 — Captions/Subtitles for video
- Mirador 4.0.0 src/config/settings.js (
filteredMotivationsのリリースデフォルト) - Mirador master src/config/settings.js (未リリース時点で
[]に変更済)