A production-ready Delphi/FMX JNI bridge for Google ML Kit Document Scanner on Android. Scan documents with automatic edge detection, perspective correction, and cropping — output as JPEG or multi-page PDF.
One unit. One singleton call. Zero UI code.
- Automatic document detection and edge recognition
- Perspective correction and cropping
- JPEG output (per page) and PDF output (multi-page)
- Gallery import support
- ML Kit availability check (
IsMLKitAvailable) - Re-entrancy protection (
IsScanning) - Configurable output directory (
OutputSubDir)
- RAD Studio 12 Athens or later
- Target platform: Android 32-bit or 64-bit
- Minimum Android API: 21 (Android 5.0)
- Google Play Services on the target device
The scanner requires two Android libraries. They are published as .aar files on
Google's Maven repository (not Maven Central — com.google.android.gms:*
artifacts are only hosted by Google):
| Dependency | Version | Direct download (.aar) |
|---|---|---|
| play-services-mlkit-document-scanner | 16.0.0 | dl.google.com |
mlkit-common (com.google.mlkit:common) |
18.11.0 | dl.google.com |
RAD Studio's Java references only accepts .jar files, so extract the
classes.jar from inside each .aar (an .aar is a ZIP archive), rename it
(e.g. play-services-mlkit-document-scanner-16.0.0.jar) and put it in your
project's lib\ folder.
Alternative: Download via Gradle
Create a build.gradle in your lib\ folder — this resolves all transitive
dependencies automatically:
plugins { id 'java' }
repositories {
google()
mavenCentral()
}
configurations { mlkit }
dependencies {
mlkit 'com.google.android.gms:play-services-mlkit-document-scanner:16.0.0'
}
task copyDeps(type: Copy) {
from configurations.mlkit
into "$projectDir/deps"
}Run: gradle copyDeps
In RAD Studio:
- Project > Options > Delphi Compiler > Other > Java references
- Add both
.jarfiles (theclasses.jarextracted from each.aar) - Set Container to
Classes.dex
Add the path to Source\ to your Delphi project search path:
Project > Options > Delphi Compiler > Search path:
<path-to>\delphi-docscanner\Source
Add the following entries inside the <application> tag of your AndroidManifest.template.xml:
<!-- Google ML Kit Document Scanner -->
<service
android:name="com.google.mlkit.common.internal.MlKitComponentDiscoveryService"
android:exported="false"
android:directBootAware="true">
<meta-data
android:name="com.google.firebase.components:com.google.mlkit.common.internal.CommonComponentRegistrar"
android:value="com.google.firebase.components.ComponentRegistrar" />
</service>
<activity
android:name="com.google.mlkit.vision.documentscanner.internal.GmsDocumentScanningDelegateActivity"
android:exported="false"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" />Important: These entries must be placed inside
<application>...</application>, not at the root level. Without them, the scanner will fail silently at runtime.
uses
mxDocScannerUnit;
procedure TForm1.btnScanClick(Sender: TObject);
begin
if not TmxDocScanner.Instance.IsMLKitAvailable then
begin
ShowMessage('ML Kit Document Scanner not available on this device.');
Exit;
end;
TmxDocScanner.Instance.StartScan(
5, // max pages
True, // allow gallery import
dsfJPEG, // output format: dsfJPEG or dsfPDF
procedure(const AFilePath: string; APageIndex, ATotalPages: Integer)
begin
// Called once per page (JPEG) or once (PDF)
ShowMessage('Page ' + IntToStr(APageIndex + 1) + ': ' + AFilePath);
end,
procedure(const AErrorMessage: string)
begin
ShowMessage('Error: ' + AErrorMessage);
end
);
end;Scanned files are saved to <SharedPictures>/<OutputSubDir>/. The default subdirectory is mxDocScanner. To change it:
TmxDocScanner.Instance.OutputSubDir := 'MyApp';Set this before calling StartScan.
| Member | Description |
|---|---|
class function Instance: TmxDocScanner |
Singleton access |
class procedure ReleaseInstance |
Free the singleton (typically not needed) |
function IsMLKitAvailable: Boolean |
Check if ML Kit Document Scanner is available on the device |
procedure StartScan(...) |
Start a document scan |
property IsScanning: Boolean |
True while a scan is in progress |
property OutputSubDir: string |
Subdirectory name for scanned files (default: 'mxDocScanner') |
| Parameter | Type | Description |
|---|---|---|
AMaxPages |
Integer |
Maximum number of pages to scan |
AGalleryImport |
Boolean |
Allow importing images from gallery |
AFormat |
TDocScanFormat |
dsfJPEG for JPEG per page, dsfPDF for multi-page PDF |
AOnPageResult |
TDocScanPageCallback |
Called per scanned page with file path, page index, and total pages. In PDF mode, JPEG callbacks fire first (for preview), then one PDF callback. |
AOnError |
TDocScanErrorCallback |
Called on error or cancellation with message |
AMode |
TDocScanMode |
Scanner mode (optional, default: dsmFull) |
TDocScanMode = (dsmBase, dsmBaseWithFilter, dsmFull);
TDocScanFormat = (dsfJPEG, dsfPDF);
TDocScanPageCallback = reference to procedure(
const AFilePath: string; APageIndex, ATotalPages: Integer);
TDocScanErrorCallback = reference to procedure(
const AErrorMessage: string);| Mode | Description |
|---|---|
dsmBase |
Basic scanning (edge detection, cropping) |
dsmBaseWithFilter |
Basic scanning + image filters |
dsmFull |
Full features: edge detection, cropping, rotation, filters (default) |
In PDF mode (dsfPDF), the scanner delivers both JPEG and PDF results:
AOnPageResultis called once per page with the JPEG file path (useful for preview)AOnPageResultis called once more with the PDF file path
This allows apps to show page previews while also receiving the final PDF for upload or storage.
| Scenario | Behavior |
|---|---|
| Device without Google Play Services | IsMLKitAvailable returns False |
| User cancels scan | Error callback fires with 'Scan cancelled', IsScanning resets to False |
| PDF mode with 1 page | Produces a single-page PDF, JPEG preview callback fires first |
| Multiple concurrent scan calls | Blocked by IsScanning check, error callback fired |
| Non-Android platform | StartScan fires error callback: 'Document Scanner is only available on Android' |
- Internal ML Kit API: The unit uses
com.google.mlkit.common.sdkinternal.MlKitContextfor manual initialization, which is an internal Google API. This is necessary because Delphi does not support Android ContentProvider-based auto-initialization. The class has been stable across ML Kit versions, but could change without notice in future SDK updates. - Google Play Services required: The scanner depends on Google Play Services. Devices without GMS (e.g., Huawei) are not supported. Use
IsMLKitAvailableto check at runtime. - Singleton pattern:
TmxDocScanneris designed for main-thread use. Do not callStartScanfrom background threads.
The singleton is automatically released in the finalization section. To release it manually:
TmxDocScanner.ReleaseInstance;MIT License — see LICENSE for details.
Copyright (c) 2026 MicrotronX - Speditionssoftware vom Profi