注釈
ビューアに注釈サポートを追加する
Doconut の注釈は二方向で機能します。ユーザーがブラウザウィジェット上で描画し、サーバーがページごとに保持するか、またはコードで注釈を作成してオープンセッションにロードします。どちらの場合もページに描画され、PDF や PNG のエクスポート時に焼き込むことができます。
注釈サポートは Annotation ライセンス機能で制御されます(有効な Temporary ライセンスの下で自動的に付与されます)。
注釈 UI を有効にする
注釈は Viewer のモジュールであり、スタンドアロンのツールバーではありません。ページには Viewer のリソース、Viewer ツールバー、Viewer のマウント、そして初期化された objViewer を含める必要があります。その後、Annotation リボンがマウントされ、同じインスタンスに添付されます。
注釈ファイルを Viewer ファイルと一緒に出力します。これらはライセンスで制御されるため、機能が付与されたときにのみタグが表示されます。MVC 5 では、コントローラ内でタグを構築します(ビューには @inject がありません):
var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
ViewBag.ViewerCss = viewer.ReferenceCss(new CssConfig
{
IncludeViewerCss = true,
IncludeAnnotationCss = true // jquery-ui.min.css + annotationBar.css
});
ViewBag.ViewerScripts = viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeViewerScripts = true,
IncludeAnnotationScripts = true, // jquery-ui, raphael.js, annotation.js
IncludeAnnotationBar = true // the embedded annotation ribbon
});
ViewBag.IsAnnotationEnabled = viewer.IsAnnotationEnabled;マークアップ内に完全な Viewer の構成を保持します:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Viewer controls, including the button that opens Annotation -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>annBarMount 内で Annotation ファイルがリボンを生成します。ボタンやダイアログをコピーする必要はありません。まず docViewer を初期化し、サーバーが注釈がライセンスされていることを確認したときにのみリボンを作成します:
@Html.Raw(ViewBag.ViewerScripts)
<script>
var annBar = null;
var currentToken = '';
var objViewer = $('#div_ctlDoc').docViewer({
BasePath: '/',
ResPath: 'doconut-res/images',
onAnnLoaded: function () { if (annBar) annBar.handleAnnLoaded(); },
onAnnSaved: function () { if (annBar) annBar.handleAnnSaved(); },
onAnnSaveError: function () { if (annBar) annBar.handleAnnSaveError(); },
onAnnClosed: function () { if (annBar) annBar.handleAnnClosed(); },
onError: function (message) { console.error('Viewer error:', message); }
});
@if ((bool)ViewBag.IsAnnotationEnabled)
{
<text>
annBar = $('#annBarMount').doconutAnnotationBar({
docId: 'ctlDoc',
getRequestParams: function () { return { token: currentToken }; },
onLayout: function () { requestAnimationFrame(function () { objViewer.Refit(); }); }
});
annBar.attach(objViewer);
</text>
}
</script>機能がない場合、ReferenceScripts はリボンのスクリプトを除外するため、サーバー側の if が保護されていない doconutAnnotationBar(...) 呼び出しがページを壊すのを防ぎます。Web Forms では、同じガードを <% if (IsAnnotationEnabled) { %> … <% } %> のように記述します。
リボンからの保存はデータを DocImage.axd(AnnSave)にポストし、ページごとのドキュメントセッションに保存します。ロード(AnnLoad)は、注釈付きページがレンダリングされると自動的に行われます。
annBar.open() と annBar.close() を使用して、独自のツールバーからリボンを開閉できます。その公開 API は次のとおりです:
| メソッド | 目的 |
|---|---|
attach(objViewer) | 初期化されたビューアにリボンを接続します;一度だけ必要です |
open() / close() | 注釈編集モードに入るまたは離れる |
reset() | リボンを閉じた編集なしの状態に戻します |
isOpen() / annotating() | リボンの状態 / ビューアの注釈編集状態を取得します |
reopenEditable() | 現在のページの注釈を編集可能なオブジェクトとして再読み込みします |
updateActionState() | ホストの変更後に保存と削除のコントロールを更新します |
headerSlot() | ホスト所有のコントロール用のオプションヘッダースロット |
onStatus、onToast、onLayout、onEditStart、onEditEnd はオプションのホストコールバックです。endpoints オブジェクトは exportPdf、exportPng、imageUpload、imageList も提供できます。設定されたエンドポイントがないコントロールは非表示のままです。
コールバックの2種類、2通りの接続方法
アノテーションコードは2つのコールバックスタイルを使用しており、逆方向に接続するとそれぞれが黙って失敗します。
onAnnLoaded、onAnnSaved、onAnnSaveError、onAnnClosedはdocViewer({...})の オプション であり、すべてのビューアコールバックと同様です。Created、Changed、Deleted、Propertiesは 名前で検索されるグローバル関数 です。ビューアの ID がイベント名の前に付加されます。#div_ctlDocのビューアの ID はctlDocなので、関数はctlDoc_Created、ctlDoc_Changed、ctlDoc_Deleted、ctlDoc_Propertiesとなります。綴りミスや名前変更があると、関数は呼び出されません。
SaveAnnotations() は保存成功かどうかを返しません。結果は onAnnSaved または onAnnSaveError で通知されます。戻り値を読むコードは「未保存の変更」フラグをクリアしません。
C# でアノテーションを作成
オープンセッションにバインドされたマネージャーを取得し、アノテーションを追加してロードします(型は using Doconut.Annotations;、Rectangle と Color は using System.Drawing; を使用)。
// POST /Annotations/LoadSample?token=...
[HttpPost]
public ActionResult LoadSample(string token)
{
var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
// Bound to the open session's page dimensions
var manager = viewer.GetAnnotationManager(token);
var pageCount = viewer.GetPageCount(token);
// One stamp per page
for (int page = 1; page <= pageCount; page++)
{
manager.Add(new StampAnnotation(page, new Rectangle(30, 20, 240, 90),
$"PAGE {page}", 28, 4, Color.Maroon)
{
Opacity = 60,
Rotate = -8
});
}
manager.Add(new NoteAnnotation(1, new Rectangle(420, 150, 220, 120),
"Loaded from C# code.", Color.FromArgb(255, 255, 255, 170), 14));
// Load into the session - the viewer fetches them, and the renderer burns them into
// image/PDF exports.
viewer.LoadAnnotationData(token, manager);
return new HttpStatusCodeResult(200);
}アノテーションタイプ
Doconut.Annotations にすべてのタイプがあり、BaseAnnotation(ページ番号と境界 Rectangle)を継承します。
| タイプ | 備考 |
|---|---|
StampAnnotation | フォントサイズ、枠線、色を持つテキストスタンプ。Opacity、Rotate をサポート |
NoteAnnotation | テキスト、背景色、フォントサイズ、TitleColor を持つ付箋ノート |
RectangleAnnotation | 枠線と塗りつぶしの色、Title/ShowTitle |
CircleAnnotation | 枠線と塗りつぶし、ShowBorder |
EllipseAnnotation | 枠線と塗りつぶし、ShowBorder |
TriangleAnnotation | 枠線色、BackColor、ShowBorder |
LineAnnotation | 幅と色を持つ直線 |
ArrowAnnotation | 矢じり付きの線。設定可能な Direction(ArrowDirection、方位、デフォルトは E) |
FreehandAnnotation | エンコードされた FreehandData ポイントからのフリーストローク |
ImageAnnotation | URL からの画像。相対 URL はアノテーション追加時に現在のリクエストのホストに対して解決されます—以下のエクスポート注記を参照 |
AnnotationManager API
| メンバー | 目的 |
|---|---|
Add(BaseAnnotation) | アノテーションをキューに追加 |
GetAnnotations() / GetAnnotations(int page) | マネージャーが保持しているアノテーションを確認 |
ClearAnnotations() / ClearAnnotations(int page) | すべて(またはページ単位)を削除 |
GetAnnotationData() / GetAnnotationData(int page) | エンコードされたアノテーションデータ — ウィジェットが消費するラッパー |
GetAnnotationXml() | XML 形式 |
Viewer はセッションに対するロードと読み取り操作を鏡像します:LoadAnnotationData(token, manager) または LoadAnnotationData(token, encodedData)、LoadAnnotationXML(token, xml)、GetAnnotationXML(token)。
アノテーションを焼き込んだエクスポート
// PDF of all pages with annotations rendered onto them
public async Task<ActionResult> ExportPdf(string token)
{
var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
byte[] pdf = await viewer.ExportAnnotationsToPdfAsync(token, zoom: 100, ct: Response.ClientDisconnectedToken);
return File(pdf, "application/pdf", "export.pdf");
}
// Or a ZIP of per-page PNGs
public async Task<ActionResult> ExportPngZip(string token)
{
var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
byte[] zip = await viewer.ExportAnnotationsToPngZipAsync(token, zoom: 100, ct: Response.ClientDisconnectedToken);
return File(zip, "application/zip", "annotations-png.zip");
}エクスポートは画面上のレンダリングと同じバーナーを使用し、同じライセンスとカスタム透かしの判断が適用されます。
社内ネットワークホストで画像注釈が「Image blocked」としてエクスポートされる
ビューアとエクスポートは ImageAnnotation の画像を異なる場所から取得します:
| 対象 | 画像をダウンロードする側 | 社内ネットワークまたは開発ホスト上 |
|---|---|---|
| ブラウザ内のビューア | ユーザーのブラウザ | 画像が表示される |
ExportAnnotationsToPdfAsync / …PngZipAsync | サーバー | 赤い "Image blocked" プレースホルダー |
サーバーが画像を取得して埋め込もうとすると、プライベートネットワークへ戻るアドレスは拒否されます。対象となるのはループバック (localhost, 127.0.0.1)、0.0.0.0、リンクローカル (169.254.x.x)、プライベートレンジ 10.x, 172.16–31.x, 192.168.x、キャリアグレード NAT (100.64.0.0/10)、および IPv6 のリンクローカルとユニークローカルアドレスです。これは、細工された画像 URL を通じて内部システムへアクセスさせることを防止するための保護であり、無効化できるスイッチはありません。
相対パスの画像 URL は現在のリクエストのホストに対して解決されるため、localhost や社内サーバー、あるいはプライベートアドレスでアクセスしたサイト上では、ビューアには画像が表示されますが、エクスポートにはプレースホルダーが入ります。これはレンダリング失敗ではなくガードが機能しているためです。エクスポートに画像を必ず含めたい場合は、パブリックアドレスを持つホストから提供し、絶対 URL を使用してください。画像 URL がまったく解決できない場合は、その注釈はエラーなしで消失します—ステータスコードではなく、描画されたピクセルやネットワークログで確認してください。
永続化ワークフロー
- ドキュメントを開き、トークンを取得します。
- 以前に保存した XML またはエンコード済みデータをそのトークンにロードします。
- ウィジェットにセッションの注釈を読み取り、編集させます。
- アプリケーションが永続化を決定したとき、
GetAnnotationXML(token)で XML を取得します。 - フラット化された成果物が必要な場合は、PDF または PNG をエクスポートします。
- ドキュメントセッションを閉じます。
ビューアトークンを永続的な注釈識別子として使用しないでください—セッションとともに失効し、アプリケーションプールのリサイクルで単一サーバー上のすべてのセッションが終了します。注釈データは自分のドキュメントおよびバージョン識別子に紐付けて保存してください。
セキュリティとレンダリングに関する注意点
- 注釈リクエストはページリクエストと同じセッションおよびトークンのセキュリティを使用します。
- ユーザーが提供した画像 URL を必ず検証・制御してください。
- 大容量のフリーハンドペイロードや高解像度エクスポートはメモリ使用量を増大させます。実際のドキュメントとズーム値で十分にテストしてください。
トラブルシューティング
| 症状 | 確認項目 |
|---|---|
| 注釈リボンが表示されない | Annotation 機能と 4 つの注釈 CSS/スクリプトフラグが有効か |
| 保存時にエラーが報告される | トークンまたはセッションの有効期限切れ、ウィジェットの BasePath が正しいか |
ctlDoc_Created(または他のイベント関数)が実行されない | 関数名は「ビューア ID + _ + イベント名」になっているか、グローバル関数として定義されているか |
| C# の注釈が表示されない | ページ番号は 1 から始まり、データがアクティブなトークンにロードされているか |
| 画像注釈が画面上には表示されるがエクスポートに含まれない | 画像のホストがプライベートまたはループバックアドレスになっていないか(上記参照) |
| 再度開いたドキュメントに注釈がない | ビューアセッション外で XML またはデータを永続化し、新しいトークンにロードしているか |
このページは役に立ちましたか?