発生した問題

Nuxt 3プロジェクトでIIIFマニフェストの画像を表示するために、Universal Viewer(UV)をiframeで埋め込んでいました。従来は外部の https://universalviewer.io/uv.html を参照していましたが、ある時点からビューアが表示されなくなりました。

ブラウザのコンソールには以下のエラーが出力されます。

SES Removing unpermitted intrinsics
UV.js:2 Unknown content type

原因の調査

universalviewer.ioのリダイレクト

まず確認したところ、universalviewer.iouniversalviewer.dev にリダイレクトされるようになっていました。

<meta http-equiv="refresh" content="0; url=https://universalviewer.dev/uv.html">

リダイレクト先の universalviewer.dev/uv.html でも同じ「Unknown content type」エラーが発生します。公式サイト自体で問題が再現する状態でした。

埋め込み用HTMLの初期化方式の違い

UV 4.xには2つのHTMLファイルが同梱されています。

  • uv.html:iframe埋め込み用。IIIFURLAdapter(true)(embeddedモード)で初期化
  • index.html:デモページ。IIIFURLAdapter()(通常モード)で初期化し、iiifManifestIdを明示的に渡す

uv.htmlの埋め込みモードでは、IIIF Presentation API 2.0のマニフェストを読み込んだ際にコンテンツタイプの判定に失敗し、「Unknown content type」が発生するようです。

一方、デモページ(index.html)と同じ初期化方式を使うと正常に動作します。Netlifyにデプロイされた uv-v4.netlify.app で確認できました。

https://uv-v4.netlify.app/#?manifest=https://kokusho.nijl.ac.jp/biblio/200017711/manifest&cv=80

URLパラメータの形式

もうひとつの違いは、URLパラメータの渡し方です。

  • uv.html(埋め込み用):?manifest=...#?cv=...(クエリパラメータ+ハッシュ)
  • index.html(デモ用):#?manifest=...&cv=...(ハッシュパラメータのみ)

動作する方式はハッシュパラメータのみで完結する形式でした。

対処方法

1. UV 4.2.1をローカルに配置

npmパッケージからUV 4.2.1の必要ファイルを public/uv/ に配置しました。

npm pack universalviewer@4.2.1
tar xzf universalviewer-4.2.1.tgz

最終的に必要なファイルは以下の4点です。

public/uv/
├── umd/                 # UV本体 + チャンクJS(約190ファイル)
├── uv.css               # スタイルシート
├── uv.html              # 埋め込みページ(カスタム版)
└── uv-iiif-config.json  # IIIF設定

cjs/esm/、デモ用のindex.htmlやコレクションJSONは不要です。

2. 埋め込みページのカスタマイズ

uv.htmlを、デモページと同じ初期化方式に書き換えました。

<script>
  document.addEventListener("DOMContentLoaded", function () {
    var urlAdapter = new UV.IIIFURLAdapter();

    var iiifManifestId =
      urlAdapter.get("iiif-content") ||
      urlAdapter.get("manifest") ||
      urlAdapter.get("iiifManifestId");

    var canvasIndex = urlAdapter.get("cv") || 0;

    var data = urlAdapter.getInitialData({
      iiifManifestId: iiifManifestId,
      canvasIndex: Number(canvasIndex),
    });

    var uv = UV.init("uv", data);
    urlAdapter.bindTo(uv);

    uv.on("configure", function ({ config, cb }) {
      cb(
        fetch("uv-iiif-config.json").then(function (response) {
          return response.json();
        })
      );
    });

    var $UV = document.getElementById("uv");

    function resize() {
      var w = window.innerWidth;
      var h = window.innerHeight;
      $UV.style.width = w + "px";
      $UV.style.height = h + "px";
      if (uv && uv.resize) {
        uv.resize();
      }
    }

    addEventListener("resize", resize);
    resize();
  });
</script>

変更点は以下の通りです。

  • IIIFURLAdapter(true)IIIFURLAdapter()(embeddedモードを無効化)
  • getInitialData({embedded: true})getInitialData({iiifManifestId, canvasIndex})(マニフェストを明示的に渡す)
  • uv-iiif-config.jsonを読み込むconfigureハンドラを追加
  • リサイズ時にuv.resize()を呼び出し

3. iframe側のURL形式を変更

Vueコンポーネント側で、URLをハッシュパラメータ形式に変更しました。

// 変更前
`https://universalviewer.io/uv.html?manifest=${field.manifest}#?cv=${cv}`

// 変更後
`/uv/uv.html#?manifest=${field.manifest}&cv=${cv}`

補足

  • 「SES Removing unpermitted intrinsics」はUV内部のセキュリティ機構(SES/Hardened JavaScript)による警告で、動作には影響しません
  • UV 4.xのumd/ディレクトリには約190のチャンクJSファイルが含まれます。UV.js単体では動作しません
  • 今回テストしたマニフェストはIIIF Presentation API 2.0形式(sc:Manifest)です。API 3.0形式での動作は未確認です