CapacitorアプリでドキュメントをPDFにスキャンするには、ネイティブのスキャナを開きます @capgo/capacitor-document-scanner、JPEGのページを返す pdf-libを組み合わせて1つのPDFを作成します。そこからPDFを書きます @capacitor/filesystem と手元のドキュメントをシェアシート、PDFビューア、またはシステムの印刷ダイアログに渡します。このガイドでは、Capacitorアプリで実行されるcodeのフローを完全に歩きます。Capacitor 8で実行され、iOSとAndroid両方で動作します。
iOSとAndroidでPDFにスキャンする方法
両方のプラットフォームでは、システムのドキュメントスキャナーが搭載されているため、エッジ検出や視点補正を自分で構築する必要はありません:
| iOS | Android | |
|---|---|---|
| ネイティブスキャナー | VisionKit VNDocumentCameraViewController |
Google ML Kit Document Scanner (Play services) |
| エッジ検出とトリミング | はい | はい |
| フィルタリング/クリーンアップ | VisionKitの組み込み | base, base_with_filter, full モード (汚れと指の除去) full) |
| 1 ページあたりのセッション制限 | 24 | 1 から 24、デフォルト 20 |
| カメラの許可を求める | はい、必要 NSCameraUsageDescription |
いいえ、スキャナーはPlay servicesで動作 |
| エミュレータ/シミュレータで動作 | いいえ | いいえ |
| プラグインからの出力 | JPEG ページ | JPEG ページ |
プラグインは画像を返しますが、PDFではありません。 それは意図的な分割です: ページサイズ、圧縮、ファイル名を制御し、同じPDF code はギャラリーから写真を選択したり、バックエンドから受け取ったファイルに使用できます。
プラグインのインストール
bun add @capgo/capacitor-document-scanner @capacitor/filesystem pdf-lib
bunx cap sync
スキャナープラグインは Capacitor メジャーバージョンを追跡するため、プラグインv8は Capacitor 8をターゲットします。 pdf-lib は、ネイティブ code を持たない純粋なJavaScriptライブラリです。つまり、WebViewで実行され、同期が必要ありません。
iOSの設定
. VisionKitがカメラを開き、iOSはキーの欠如によりアプリを終了します: ios/App/App/Info.plistVisionKitはカメラを開き、iOSはキーが欠落している場合にアプリを終了します。
<key>NSCameraUsageDescription</key>
<string>We use the camera to scan your documents.</string>
Capacitor ビルド Capgo ビルド クラウドで署名されたiOSバイナリを生成できます。
Androidの設定
何も追加する必要はありません。 AndroidManifest.xml. ML Kit スキャナーは、Google Play サービス モジュールで、初回使用時にダウンロードされ、カメラを管理するため、アプリはカメラを要求しません。 CAMERA. デバイスには Google Play サービスが必要であり、これはほとんどのエミュレータと de-Googled スマートフォンを除外します。Play サービスが欠如している市場に配信する場合は、フォールバック (平凡なカメラキャプチャ) を計画してください。
ページをスキャンする
開始するには、スキャナーを開く関数を作成し、ページのパスを返すか、または null ユーザーがキャンセルしたとき:
import {
DocumentScanner,
ResponseType,
ScannerMode,
ScanDocumentResponseStatus,
} from '@capgo/capacitor-document-scanner';
export async function scanPages(): Promise<string[] | null> {
const result = await DocumentScanner.scanDocument({
responseType: ResponseType.ImageFilePath,
maxNumDocuments: 20,
letUserAdjustCrop: true,
scannerMode: ScannerMode.Full, // Android only
croppedImageQuality: 90, // Android only
});
if (result.status === ScanDocumentResponseStatus.Cancel) {
return null;
}
return result.scannedImages ?? [];
}
いくつかのオプションは知っておく価値があります:
responseType:ImageFilePath(デフォルト) ファイルパスを返すBase64returns base64 strings. Prefer file paths for multi-page documents. Base64 strings for 20 high resolution pages can reach tens of megabytes and every byte crosses the Capacitor bridge.maxNumDocuments: を設定します1base64文字列を返すreviewCapturedDocumentシングルページのフロー (レシートや ID カード) の場合に使用します。brightnessとcontrast: 画像処理が適用された。1.2を1.5よく、消し消されていたレシートの問題を解決するのに役立ちます。scannerMode(Android):Full機械学習ベースのクリーンアップを追加します。Base速度が速くなり、画像が変更されません。
ファイルの保存場所はプラットフォームによって異なります。Androidの場合、ページはアプリキャッシュディレクトリに保存されます。iOSの場合、プラグインはアプリのドキュメントディレクトリに保存されます。どちらの場所も長期的な保存場所ではありません。したがって、以下のフローでは、PDFを作成し、保存先を指定し、ページ画像を削除します。
ページを1つのPDFに組み合わせる
pdf-lib JPEGを直接埋め込むことができるため、PDFサイズはページサイズの合計に近づきます。各ページを読み取ります。 Capacitor.convertFileSrc,
import { Capacitor } from '@capacitor/core';
import { PDFDocument } from 'pdf-lib';
const A4 = { width: 595.28, height: 841.89 }; // points
export async function pagesToPdf(paths: string[]): Promise<string> {
const pdf = await PDFDocument.create();
pdf.setTitle('Scanned document');
pdf.setCreator('My Capacitor App');
for (const path of paths) {
const bytes = await fetch(Capacitor.convertFileSrc(path)).then((r) => r.arrayBuffer());
const image = await pdf.embedJpg(bytes);
// Fit the image inside an A4 page, keeping its aspect ratio
const scale = Math.min(A4.width / image.width, A4.height / image.height);
const width = image.width * scale;
const height = image.height * scale;
const page = pdf.addPage([A4.width, A4.height]);
page.drawImage(image, {
x: (A4.width - width) / 2,
y: (A4.height - height) / 2,
width,
height,
});
}
// Base64 without the data URI prefix, ready for Filesystem.writeFile
return pdf.saveAsBase64();
}
URLでアクセスできるように、ネイティブパスをURLに変換します。 pdf.addPage([image.width, image.height]) と、画像をフルサイズで描画します。領収書はこのように見る方が良いでしょう。なぜなら、長く細長い形だからです。
Alternative: PDFを直接レンダリングします。
アプリがすでにHTMLからPDFを作成している場合 (請求書、レポート)、そのパスを再利用できます。 @capgo/capacitor-pdf-generator. HTML文字列をプラットフォームの印刷エンジンでレンダリングし、base64で返します。
import { PdfGenerator } from '@capgo/capacitor-pdf-generator';
import { Capacitor } from '@capacitor/core';
const html = `
<html><body style="margin:0">
${paths
.map((p) => `<img src="${Capacitor.convertFileSrc(p)}" style="width:100%;page-break-after:always" />`)
.join('')}
</body></html>`;
const result = await PdfGenerator.fromData({
data: html,
documentSize: 'A4',
orientation: 'portrait',
type: 'base64',
});
if (result.type === 'base64') {
// result.base64 contains the PDF
}
この機能は、各ページにカバー画像、タイトル、または日付を含むフッターを表示したい場合に便利です。純粋な画像からPDFを作成する場合、 pdf-lib より詳細なページの幾何学と画像圧縮の制御が可能です。
PDFを保存します。
base64の出力を書き込むには @capacitor/filesystem. 保存期間に基づいてディレクトリを選択します:
| ディレクトリ | Android | iOS | Capacitorアプリで使用する |
|---|---|---|---|
Directory.Cache |
アプリキャッシュ、OSによって削除可能 | Library/Caches, OSによって削除可能 |
アップロードまたは即時共有したPDF |
Directory.Data |
アプリのプライベートファイル、アンインストール時に削除 | アプリ Documents フォルダ、iCloudにバックアップ |
アプリが管理し、後で表示するPDF |
Directory.LibraryNoCloud |
アプリのプライベートファイル | Library/NoCloud, バックアップされない |
ダウンロードできる大きなPDF |
iOSでは、 Directory.Data そして Directory.Documents 同じフォルダを指す。追加 UIFileSharingEnabled そして LSSupportsOpeningDocumentsInPlace に Info.plist ユーザーがファイルアプリでPDFを表示するようにするかどうか
import { Filesystem, Directory } from '@capacitor/filesystem';
export async function savePdf(base64: string, name: string) {
const fileName = `${name.replace(/[^a-z0-9-_]/gi, '_')}.pdf`;
const { uri } = await Filesystem.writeFile({
path: `scans/${fileName}`,
data: base64,
directory: Directory.Data,
recursive: true,
});
return uri; // file:// URI you can share, open, or print
}
export async function deletePages(paths: string[]) {
await Promise.all(
paths.map((path) => Filesystem.deleteFile({ path }).catch(() => undefined)),
);
}
ネイティブプラットフォームでは writeFile 文字列にオプションがなければbase64と扱い、バイナリバイトを書き込む。PDFの場合は、ファイルが破損するため encoding ユーザーがAndroidのダウンロードフォルダでPDFを探すことができるようにする場合は、 Encoding.UTF8 PDFの場合、ファイルが破損する可能性があります。
context @capgo/capacitor-file-sharercontext save() context
フローを組み合わせてください
export async function scanToPdf(title: string) {
const pages = await scanPages();
if (!pages || pages.length === 0) return null;
try {
const base64 = await pagesToPdf(pages);
return await savePdf(base64, title);
} finally {
await deletePages(pages);
}
}
ボタンに接続し、実行中はスピナーを表示してください pagesToPdf Androidの標準的なスマートフォンでは、JPEGデータはコピーされるため、10ページは数秒以内に完了します。
PDFを共有、開く、または印刷してください
システムの共有シートで共有してください
import { FileSharer } from '@capgo/capacitor-file-sharer';
await FileSharer.share({
filename: 'contract.pdf',
path: pdfUri,
contentType: 'application/pdf',
subject: 'Signed contract',
});
@capgo/capacitor-file-sharer ファイルをキャッシュフォルダにコピーし、キャッシュフォルダを介してサーバーする FileProvider Androidの場合、PDFがキャッシュフォルダに存在する場合、キャッシュフォルダのパスを指定するだけで済みます file_paths.xmlキャッシュフォルダのパスを指定するだけで済みます Directory.DataPDFがキャッシュフォルダに存在する場合、キャッシュフォルダのパスを指定するだけで済みます @capacitor/share PDFがキャッシュフォルダに存在する場合、キャッシュフォルダのパスを指定するだけで済みます files PDFがキャッシュフォルダに存在する場合、キャッシュフォルダのパスを指定するだけで済みます android/app/src/main/res/xml/file_paths.xml.
PDFがキャッシュフォルダに存在する場合、キャッシュフォルダのパスを指定するだけで済みます
iOSではQuick Lookに、Androidでは標準のPDFビューアにファイルを渡します。
bun add @capacitor-community/file-opener
bunx cap sync
import { FileOpener } from '@capacitor-community/file-opener';
await FileOpener.open({
filePath: pdfUri,
contentType: 'application/pdf',
});
印刷
@capgo/capacitor-printer iOSとAndroidで UIPrintInteractionController iOSと PrintManager Androidで
import { Printer } from '@capgo/capacitor-printer';
await Printer.printPdf({
name: 'Signed contract',
path: pdfUri,
});
PDFをアップロード name バックエンドにPDFを送信するには、PDFを読み戻して
とともに送信します、または大きなファイルのアップロードにnativeアップローダーを使用して、アプリがバックグラウンドに移動してもアップロードが続行されるようにします。
詳細は Blob __CAPGO_KEEP_0__ fetch__CAPGO_KEEP_1__
import { Uploader } from '@capgo/capacitor-uploader';
const { id } = await Uploader.startUpload({
filePath: pdfUri,
serverUrl: 'https://api.example.com/documents',
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
mimeType: 'application/pdf',
});
See the Capacitor アップローダープラグイン 進捗イベントとマルチパートアップロードのために
スキャンしたPDFを検索可能にする
イメージのみのPDFは検索やコピーができません。ユーザーがスキャンを検索できることを期待している場合:
- 各ページイメージを削除する前に、機器上のオプションとしてAndroidのML Kit Text RecognitionやiOSのVision Frameworkを通じてコミュニティから利用できるCapacitor プラグインを使用して、各ページイメージにOCRを実行するか、サーバーサイドのOCRサービスを使用する。
- 認識されたテキストをPDFのパスとともにデータベースに保存する。
- PDF自体にテキストを含める必要がある場合は、サーバーサイドでOCRツールを使用して、不視覚的なテキストレイヤーを生成する。例えば、OCRmyPDFを使用する。
ほとんどのアプリでは、ステップ2が十分であり、アプリに2番目の大きなモデルを配信するのを避けることができます。
トラブルシューティング
「Android エミュレータではドキュメント スキャナーがサポートされていません」「Android エミュレータでドキュメント スキャナーがサポートされていません」
期待どおりです。Google Play サービスを搭載した物理デバイスを使用してください。: NSCameraUsageDescription は欠けている Info.plist, またはユーザーがカメラアクセスを拒否した場合に
embedJpg 設定プライバシー Capacitor.convertFileSrc(path) カメラ
“SOI not見つかりました”: lower croppedImageQuality を表示し、SafariまたはChromeのインスペクターで開きます。 @capgo/capacitor-file-compressor PDFはとても大きい
: 低く(Android) またはページを再圧縮して page.setRotation(degrees(90)) にします。品質70から80は、一般的にテキストドキュメントで読みやすいです。 reviewCapturedDocument: true.
Androidで共有が失敗する FileUriExposedException または「設定されたルートを検出できません」: パスが宣言されたフォルダの外側に渡されました。 file_paths.xml を @capacitor/share。ファイルシェアプラグインを使用するか、PDFを Directory.Cache にコピーしてください。
iOSの古いページ画像がストレージを占める: スキャナはJPEGをDocumentsディレクトリに書き込む。PDFを構築した後、削除するようにします。 scanToPdf.
を参照してください。
スキャンフローの更新 Capgo ライブアップデート __CAPGO_KEEP_0__のライブアップデートを使用すると、PDFレイアウトまたはアップロードエンドポイントの修正を、同じ日にすべてのユーザーに配信できます。ネイティブの変更 (プラグインの追加、エディット Info.plist)通常のビルドを通して行きます。 ドキュメント スキャナーのドキュメント 必要に応じてスキャナーをさらに調整するには、すべてのオプションを表示します。