きっかけ
自作の帳票エンジン「ACR (AcrossReport)」は、Google Skiaをレンダリングコアに据え、JSONを中間描画モデルとして扱うことで「実機がなくても1ドットも狂わないWYSIWYGプレビュー」を実現してきた。POS・ラベル・レシート印刷、そしてActiveReports互換の帳票designerと、30年分の現場知見を積み上げてきたエンジンだ。
このACRのレンダリングコアを、VS Codeの中に持ち込んだのが「ACR Viewer for VSCode」である。きっかけは単純で、VS Code自体にMarkdownやHTML、テキストファイルをまともに印刷・PDF化する手段がないことだった。
設計方針:WASMではなくネイティブアドオン
当初はChrome拡張(ACR Report Renderer)と同じくWASM化したSkiaコアをブラウザ上で動かす構成を想定していたが、VS Code拡張のホストはブラウザのサンドボックスではなくNode.jsで動く。であれば、AcrossReportDesigner(Avalonia)が使っているのと同じRustのcdylibビルドをNAPI-RS経由でそのままバインドできる。WASM経由のオーバーヘッドや制約を避けられるため、ネイティブアドオン方式に切り替えた。
Markdown/HTMLからACRのJSON中間モデルへの変換もRust側(acr_mdクレート、pulldown-cmarkベース)に寄せ、Node.js側はエントリーポイントを1つ呼ぶだけの薄いクライアントにした。ページ送りロジックも新規実装はせず、acr_core既存の高さベース自動改ページ(place_section)をそのまま再利用する形に倒している。「現場を動かす確かなコード」を壊さず、既存資産に載せるという方針を徹底した結果だ。
名前衝突との戦い
実装よりむしろ苦労したのはMarketplaceへの登録段階だった。
• name: "acr-viewer" / displayName: "ACR Viewer" は検索には出てこないのに公開時に弾かれる、いわゆる"ゾンビ予約"状態だった
• 次に試した "ACR Viewer for VSCode" も、既存の"3D Viewer for VSCode"と類似しすぎているとして却下
最終的に name: "acr-viewer-vscode" / displayName: "ACR Report Viewer" に変更してようやく通った。プラットフォームごとにvsce package --target でビルドし、vsce publish --packagePath で個別に公開する必要があり(--targetをまとめて渡す方式は使わなかった)、Windows/Linux/macOS(darwin-arm64)の3ビルドをそれぞれ手で上げていった。
クロスプラットフォームビルドの現実
NAPI-RSネイティブアドオンなので、当然OS・アーキテクチャごとにビルドが要る。
• Ubuntu(x86_64)は開発機でそのままビルド
• Mac mini M1(aarch64-apple-darwin)はHomebrew/nvm未導入からのセットアップが必要で、rustupをedition2024対応のstableまで上げてようやく通った
• Windowsは別途
extension.ts側がNAPI-RS標準の自動OS判定ディスパッチャ(index.js)を使わず、native/acr_vscode_native.nodeという固定パスをロードする実装になっているため、OSごとに別々の.vsixを作る必要がある。地味だが繰り返し踏んだ罠が、@napi-rs/cli build --release>が出力するのはindex.nodeなのに、拡張側はacr_vscode_native.nodeという名前を期待している点で、リネームを忘れるたびに.vsixが古いバイナリのまま再パッケージされていた。
Skiaの安定性を作り込む
VS Code拡張ホストは、Node.jsプロセスの中でNAPI-RS越しにRustを呼んでいる。ここで見つかった問題が、テンプレートがWindows専用フォント(Segoe UI/Consolasなど)を指定していた場合、Skiaのフォントフォールバックがpanic!していたことだ。RustのpanicはFFI境界を越えてunwindできないため、これがそのままNode.jsプロセス全体を無言で落としていた。エラーも出ず、コンソールにも何も出ず、ただ拡張ホストが消える。原因究明に時間がかかったが、panicを警告ログ+Skiaのlegacy_make_typefaceフォールバックに置き換えることで解決した。
CJKのグリフが表示されない、いわゆる「トーフ」問題も、Typeface::unichar_to_glyphによる文字単位のグリフカバレッジチェックとmatch_family_style_characterフォールバックで解消し、Ubuntu・Mac mini M1・Windowsの3機種すべてで動作確認まで済ませている。
キーバインドも一発では決まらない
• 当初のCtrl+Shift+A/Dは他バインドと衝突
• Ctrl+Alt+A/Dに変更したが、macOSではOption+Aが特殊文字入力(å)に化けて発火しない
• Ubuntu(GNOME)ではCtrl+Alt+Dが「デスクトップを表示」と衝突
最終的にWindows/Mac/Linux共通でCtrl+Alt+0(プレビュー)・Ctrl+Alt+9(PDFエクスポート)に統一した。OSごとに個別定義するのではなく、全プラットフォームで衝突しない組み合わせを探す方向に倒したのがポイントだ。
配布チャネル:Marketplace / Open VSX / GitHub Releases
ソースは非公開のまま、独自EULA(実行可・逆コンパイル禁止・再配布禁止)で配布する方針を最終決定した。
ka
• VS Code Marketplace: across-systems.acr-viewer-vscode として公開。出先での利用も、手動ビルドの.vsixではなく正式なMarketplaceインストールにできることが、セキュリティ運用上のハードルを下げる意味でも重要だった
• Open VSX: GitHubアカウントをEclipseアカウントに連携し、Eclipse Foundation Open VSX Publisher Agreementに署名してacross-systems名前空間を取得。Open VSXはライセンス未設定の拡張を弾くため、LICENSE.txtを同梱しpackage.jsonのlicenseフィールドを"SEE LICENSE IN LICENSE.txt"とした
• GitHub Releases(acrossreport/acr-viewer-vscode): OS別.vsixをそのまま添付する形で継続
.vsixの命名も、当初の手動リネーム(acr-viewer-0.0.1-.vsix)から、vsce package --target が自動生成するacr-viewer-vscode--.vsix形式に統一し、3つの配布チャネル間で表記を揃えた。
おわりに
「ACR Viewer for VSCode」は単なる印刷拡張ではなく、ACRという帳票エンジンの世界にユーザーを引き込む入口(オンボーディング)として位置づけている。
README冒頭も「VS Code拡張」という説明から、「ACR for Code — Report as Code Platform」(Markdown → ACR Report Definition → Render Once → PDF/Label/Receipt)というポジショニングに変更した。
実装そのものより、Marketplaceの名前衝突、OSごとのネイティブビルド、Skiaのpanic対策、キーバインドの衝突回避といった「公開して初めて分かる」泥臭い部分に時間を取られた、というのが正直な実感だ。今後はソース管理をネットワークドライブ経由の手動コピーから単一Gitリポジトリに一本化し、3機種すべてが同じリポジトリからクローンする体制に移行を進めている。