pdf-annotations について
Android 開発者向けに、PDF の注釈とページオブジェクトの編集方法を案内するガイドです。プラットフォームと拡張機能のバージョンに応じた API の選択、注釈操作、描画、ファイルやページの制約を扱います。
主な機能
- 01API と拡張バージョンに応じた入口選択
- 02ページ注釈の読み取りと編集
- 03ページオブジェクトと変換の操作
- 04注釈描画フラグの設定
- 05ファイル記述子とページの制約への対応
活用シーン
- 01対応端末での PDF ハイライト追加
- 02既存注釈の更新や削除
- 03位置指定によるページオブジェクトの選択と編集
- 04注釈の非表示や書き込み失敗の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rcosteira79/ android-skills --skill pdf-annotationsインストール後、Claude Code で/pdf-annotationsを入力して読み込まれたか確認します。
詳細
プラットフォームでの PDF 注釈とページオブジェクトの編集
Android では API 36.1 以降、PDF の注釈をネイティブに編集できます。それ以前はプラットフォームでの対応はレンダリングのみで、ドキュメントにもそのように記載されています。Jetpack ビューアーガイドで PDF を表示するのは PdfViewerFragment であり、編集モードには明示的に対応していません。注釈は android.intent.action.ANNOTATE インテントを使って他のアプリに引き渡します
[kb://android/develop/ui/views/layout/pdf/implement-pdf-viewer]。以下のバージョン条件を確認する前に、注釈には「サードパーティーライブラリを使う」と結論づけないでください。
バージョン条件(最初にエントリーポイントを決める)
並行して提供されている 2 つのレンダラーは、同じ編集 API
(android.graphics.pdf.component)を公開しています。
| エントリーポイント | 編集の利用可否 | デバイス要件 |
|---|---|---|
PdfRenderer.Page | API 36.1 以降 | プラットフォームリリース |
PdfRendererPreV.Page | SDK 拡張機能 18 | PDF モジュールが更新済みの API 31 以降(PreV 自体には拡張機能 13 が必要) |
SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) を使って実行時に条件を判定します
[kb://android/guide/sdk-extensions/index]。拡張機能 18 は Mainline アップデートを通じて API 31 までさかのぼって利用できるため、ほとんどのデバイスをカバーするのは PdfRendererPreV の分岐です。
ワークフロー:開く → 編集 → 書き込む
// The fd must be seekable — pipes/sockets are rejected at construction.
ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY).use { fd ->
PdfRenderer(fd).use { renderer -> // or PdfRendererPreV on the ext-18 branch
renderer.openPage(0).use { page ->
// Only SUPPORTED annotation types are listed. The pairs are
// android.util.Pair — NO Kotlin destructuring (no component1/2);
// read them with .first / .second.
val existing = page.getPageAnnotations()
val staleId: Int? = existing.firstOrNull { it.second is FreeTextAnnotation }?.first
val highlight = HighlightAnnotation(boundsList) // List<RectF> quads
val id = page.addPageAnnotation(highlight)
check(id != -1) { "annotation rejected" } // add returns -1, it does NOT throw
highlight.color = newColor
page.updatePageAnnotation(id, highlight) // by id -> Boolean success
staleId?.let { page.removePageAnnotation(it) } // by id, not list position
}
// Write BEFORE close(), to a separate writable fd; the boolean is
// removePasswordProtection (IOException if true and the doc stays encrypted).
ParcelFileDescriptor.open(
dest,
ParcelFileDescriptor.MODE_READ_WRITE or ParcelFileDescriptor.MODE_CREATE,
).use { out ->
renderer.write(out, false)
}
}
}
注意事項:
addPageAnnotationは注釈 ID を返します。追加できない場合は-1を返します。戻り値を確認してください。例外が発生するのは、引数が null、UNKNOWNタイプ、またはすでに追加済みの場合のみです(IllegalArgumentException)。- 注釈インスタンスはドキュメント全体で一度だけ追加できます。「このページまたは別のページですでに追加済み」の注釈を追加すると例外が発生します。追加のたびに新しいコンポーネントオブジェクトを作成してください。削除済みのオブジェクトを別のページで再利用しないでください。
update/removeではリストの位置ではなく ID を使います。getPageAnnotations()が返すのは API がモデル化している注釈タイプのみのため、未対応の注釈が含まれるページでは ID に抜けがあります。Pair.firstの ID を安定したハンドルとして使ってください。- 一度に開けるページは 1 つだけです。既存の
PdfRendererの制約(「一度に開けるページは 1 つのみ」)は引き続き適用されます。次のページを開く前に、現在のページを閉じてください。 close()の前にwrite()を呼び出してください。書き込み先には、元とは別の、書き込み可能でシーク可能な fd が必要です。閉じた後に呼び出すとIllegalStateExceptionが発生します。- ペアの型は
kotlin.Pairではなくandroid.util.Pairです。Kotlin の分解構文(for ((id, a) in …))ではコンパイルできません。.first/.secondを使ってください。
編集した内容のレンダリング
注釈タイプをレンダリングするには、RenderParams.renderFlags に該当するフラグを含める必要があります。FLAG_RENDER_TEXT_ANNOTATIONS/FLAG_RENDER_HIGHLIGHT_ANNOTATIONS に加えて、36.1 では FLAG_RENDER_FREETEXT_ANNOTATIONS/FLAG_RENDER_STAMP_ANNOTATIONS が追加されています。追加したスタンプやフリーテキストがプレビュー用ビットマップに「表示されない」場合、追加に失敗したのではなく、レンダリングフラグが不足していることがほとんどです。RenderParams.Builder.setRenderFlags(...) で該当するフラグを設定し、render(bitmap, clip, transform, params) オーバーロードで再レンダリングしてください。レンダーモード定数は RenderParams(および旧来の PdfRenderer.Page)にあります。PdfRendererPreV.Page にはないため、PreV の分岐では RenderParams.Builder(RenderParams.RENDER_MODE_FOR_DISPLAY) と記述してください。PdfRendererPreV.Page を介して指定するとコンパイルできません。
コンポーネントの用語(android.graphics.pdf.component)
HighlightAnnotation(List<RectF>)— 四角形のリストで、ハイライトされた範囲ごとに 1 つの矩形を指定します。FreeTextAnnotation(RectF, String)— 境界とテキストです。テキストの色と背景色を設定できます。StampAnnotation(RectF)— コンテナーです。クラスの仕様上、子として追加できるのはPdfPagePathObject、PdfPageTextObject、PdfPageImageObjectのみです(addObject)。- ページオブジェクトはページに直接追加することもできます(
addPageObject/getPageObjects/updatePageObject/removePageObject。アノテーションと同じ ID ベースの仕様です)。また、変換行列を保持します(PdfPageObject.setMatrix/transform)。PdfPagePathObjectのストロークは、デフォルトで不透明な sRGB の黒です。 Page.getTopPageObjectAtPosition(PointF, …)は、最前面のオブジェクトをヒットテストします。タップして選択してから更新または削除する際に使用してください。
対象外(別の方法を使用)
PDF の新規作成(PdfDocument のキャンバス API)、フォームへの入力(API 35 の applyEdit)、テキストの抽出/検索、ビューアー UI の組み込み(表示専用の PdfViewerFragment[kb://android/develop/ui/views/layout/pdf/implement-pdf-viewer])は、それぞれ別の領域です。サードパーティ製 SDK が適切なのは、バージョンの要件を満たさない場合、またはコンポーネント API でモデル化されていない種類のアノテーションを扱う場合に限られます。