見出し画像

JavaScriptによるRTFからPDFへの変換

はじめに

Webアプリケーションにおけるドキュメント形式の変換処理は、フロントエンド開発において常に課題となってきました。従来のアプローチではサーバーサイドのAPIに依存するのが一般的でしたが、そのため追加のサーバーコストやネットワークレイテンシが発生していました。WebAssembly技術の成熟に伴い、現在ではブラウザ側で直接RTFからPDFへの変換を実行することが可能です。本稿では、WASMベースの実装アプローチについて紹介します。


環境準備とインストール・設定

実装を始める前に、以下の準備作業を完了させる必要があります。

1. 依存パッケージのインストール

npmを使用してドキュメント処理ライブラリをインストールします。

npm i spire.office

このパッケージにはさまざまなドキュメント形式の処理機能が含まれていますが、本稿ではRTFからPDFへの変換機能のみを使用します。

2. WASMリソースファイルの配置

インストール後、必要なランタイムファイルをプロジェクトの静的ディレクトリ(例:public/)にコピーする必要があります。対象ファイルは以下のとおりです。

  • spire.doc.js

  • spire.common.js

  • Spire.Doc.Wasm.zip

  • Spire.Common.Wasm.zip

  • _framework/

これらのファイルをpublic/ディレクトリにコピーした後のディレクトリ構成例は以下の通りです。

public/
├── spire.doc.js
├── spire.common.js
├── Spire.Doc.Wasm.zip
├── Spire.Common.Wasm.zip
├── _framework/
│   └── ...
└── static/
    ├── font/          # フォントファイル(times.ttf 等)を格納
    └── data/          # 変換対象のRTFサンプルファイルを格納(任意)

3. フォントファイルの準備

PDF内のテキストが正しくレンダリングされるようにするため、必要なTrueTypeフォントファイルを静的ディレクトリ(上記例ではpublic/static/font/)に配置します。本稿の例ではTimes New Roman系フォントを使用しますが、実際のドキュメント内容に応じて他のフォントに置き換えることも可能です。

注意:フォントファイルはそれぞれのライセンス条項に従う必要があります。アプリケーション内でこれらのフォントを使用する権利があることをご確認ください。

以上の準備が整ったら、変換ロジックの実装に進みます。


技術選定とアーキテクチャ

本アプローチではWebAssembly(WASM)技術を採用し、成熟したドキュメント処理ライブラリをWASMモジュールにコンパイルしてブラウザ上で動作させます。全体的なアーキテクチャは以下の通りです。

  • ローディング層:動的importを用いてWASMモジュールを非同期で読み込みます

  • 仮想ファイルシステム(VFS):ブラウザのメモリ内にファイルシステムをエミュレートし、WASMモジュールが読み書きできるようにします

  • 変換エンジン:WASMベースのドキュメント処理コアであり、RTFを解析してPDFを生成します

  • 出力層:VFSから生成されたPDFを読み取り、ダウンロードをトリガーします


コアコードの解説

1. WASMモジュールのロード

React Hooksを使用してモジュールのロード状態を管理し、動的importによってオンデマンドで読み込みます。

useEffect(() => {
  (async () => {
    try {
      const publicUrl = process.env.PUBLIC_URL || '';
      // 动态导入 WASM 模块
      const spireModule = await import(
        /* webpackIgnore: true */ `${publicUrl}/spire.doc.js`
      );
      const rawModule = spireModule.default || spireModule;
      // 初始化 WASM 实例,指定 .wasm 文件的位置
      window.wasmModule = typeof rawModule === 'function'
        ? await rawModule({ 
            locateFile: p => p.endsWith('.wasm') 
              ? `${publicUrl}/${p}` 
              : p 
          })
        : rawModule;
      setWasmModule(window.wasmModule);
    } catch (error) {
      console.error('Failed to load WASM module:', error);
    }
  })();
}, []);

ここで重要なのはlocateFile関数です。この関数はWASMランタイムに対して.wasmバイナリファイルをどこから読み込むかを指示します。/* webpackIgnore: true */コメントにより、Webpackがこの動的パスを解析しようとするのを防ぎます。

2. 仮想ファイルシステム(VFS)

WASMモジュールはサンドボックス環境で動作するため、ホストOSのファイルシステムに直接アクセスできません。そのため、WASMのメモリ内に仮想ファイルシステム(VFS)を構築し、必要なファイルをそこに書き込む必要があります。

// 加载字体文件到 VFS
await window.spire.FetchFileToVFS(
  'times.ttf',           // 文件名
  '/Library/Fonts/',     // VFS 中的目标路径
  `${publicUrl}/static/font/`  // 宿主环境中的资源路径
);
await window.spire.FetchFileToVFS('timesbd.ttf', '/Library/Fonts/', `${publicUrl}/static/font/`);
await window.spire.FetchFileToVFS('timesbi.ttf', '/Library/Fonts/', `${publicUrl}/static/font/`);
await window.spire.FetchFileToVFS('timesi.ttf', '/Library/Fonts/', `${publicUrl}/static/font/`);

フォントファイルはPDF生成において極めて重要であり、出力ドキュメント内のテキストが正しくレンダリングされることを保証します。

3. ドキュメントの読み込みと変換

const convertRtfToPdf = async () => {
  const wasmModule = window.wasmModule.spiredoc;
  if (!wasmModule) return;

  // 将输入文件写入 VFS
  await window.spire.FetchFileToVFS(
    'input.rtf', 
    '', 
    `${process.env.PUBLIC_URL}/static/data/`
  );

  // 创建 Document 实例
  const doc = new wasmModule.Document();
  
  // 从 VFS 加载 RTF 文件
  doc.LoadFromFile('input.rtf');
  
  // 保存为 PDF 格式
  const outputFileName = 'RtfToPdf.pdf';
  doc.SaveToFile({ 
    fileName: outputFileName, 
    fileFormat: wasmModule.FileFormat.PDF 
  });
  
  // 从 VFS 读取生成的 PDF
  const pdfData = window.dotnetRuntime.Module.FS.readFile(outputFileName);
  
  // 创建 Blob 并下载
  const blob = new Blob([pdfData], { type: 'application/pdf' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = outputFileName;
  a.click();
  
  // 清理
  URL.revokeObjectURL(url);
  doc.Dispose();
};

4. リソース管理とクリーンアップ

WASMモジュールが割り当てたメモリは手動で解放する必要があります。解放しないとメモリリークが発生する可能性があります。

// 清理 VFS 中的文件
window.dotnetRuntime.Module.FS.unlink('input.rtf');
window.dotnetRuntime.Module.FS.unlink('RtfToPdf.pdf');

// 释放 Document 对象
doc.Dispose();

ブラウザ互換性:本アプローチはWebAssemblyおよびBlob URL機能に依存しており、すべてのモダンブラウザ(Chrome、Firefox、Safari、Edge)で動作します。


まとめ

WebAssembly技術を活用することで、ブラウザ上でRTFからPDFへの変換を実現し、サーバー依存を排除してアーキテクチャの複雑さを低減することができます。このアプローチの主な利点は以下のとおりです。

  • 低レイテンシ:すべての処理がローカルで完了し、ネットワークリクエストが不要

  • 低コスト:変換サーバーを維持する必要がない

  • 高いプライバシー:ユーザードキュメントをサードパーティのサービスにアップロードする必要がない

もちろん、このアプローチには適用範囲の限界もあります。非常に大きなドキュメントや複雑なレイアウトの場合は、サーバーサイドのソリューションと組み合わせて使用することも検討すべきです。どの方式を選ぶかは、具体的なビジネスシナリオと技術的制約に基づいてバランスを取ることが重要です。

いいなと思ったら応援しよう!