PDF stamp annotations on Android
Nutrient supports stamp annotations, which you can customize to your liking. To change the default set of stamp annotations available for your application, you have to provide a StampAnnotationConfiguration
in the AnnotationConfigurationRegistry
:
// Adding custom subject stamps. val items = listOf( StampPickerItem.fromTitle(this, "Great!").build(), StampPickerItem.fromTitle(this, "Stamp!").build(), StampPickerItem.fromTitle(this, "Like").build() ) // Available stamps can be configured through the `PdfFragment`. requirePdfFragment().annotationConfiguration.put( AnnotationType.STAMP, StampAnnotationConfiguration.builder(this) // Here you set the list of stamp picker items that are going to be available in the stamp picker. .setAvailableStampPickerItems(items) .build() )
// Create list of stamps that are going to be added to the picker. final List<StampPickerItem> items = new ArrayList<>(); // Adding custom subject stamps. items.add(StampPickerItem.fromTitle(this, "Great!").build()); items.add(StampPickerItem.fromTitle(this, "Stamp!").build()); items.add(StampPickerItem.fromTitle(this, "Like").build()); // Available stamps can be configured through the `PdfFragment`. getPdfFragment().getAnnotationConfiguration().put( AnnotationType.STAMP, StampAnnotationConfiguration.builder(this) // Here you set the list of stamp picker items that are going to be available in the stamp picker. .setAvailableStampPickerItems(items) .build() );
Call
StampPickerItem.getDefaultStampPickerItems()
to retrieve the default set already bundled in the library.
Default stamp annotations
Nutrient comes with some out-of-the-box stamp annotations available in the stamp picker dialog:
val items = mutableListOf<StampPickerItem>() // Standard stamps. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.APPROVED).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.NOT_APPROVED).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.DRAFT).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.FINAL).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.COMPLETED).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.CONFIDENTIAL).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.FOR_PUBLIC_RELEASE).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.NOT_FOR_PUBLIC_RELEASE).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.FOR_COMMENT).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.VOID).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.PRELIMINARY_RESULTS).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.INFORMATION_ONLY).build()) // Tick and cross stamps. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.ACCEPTED).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.REJECTED).build()) // Signature stamps. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.INITIAL_HERE).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.SIGN_HERE).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.WITNESS).build()) // Custom stamp. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.CUSTOM).build()) // Revised/rejected stamps with localized datetime subtext. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.REVISED).withDateTimeSubtitle(true, true).build()) items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.REJECTED).withDateTimeSubtitle(true, true).build())
final List<StampPickerItem> items = new ArrayList<>(); // Standard stamps. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.APPROVED).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.NOT_APPROVED).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.DRAFT).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.FINAL).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.COMPLETED).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.CONFIDENTIAL).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.FOR_PUBLIC_RELEASE).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.NOT_FOR_PUBLIC_RELEASE).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.FOR_COMMENT).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.VOID).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.PRELIMINARY_RESULTS).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.INFORMATION_ONLY).build()); // Tick and cross stamps. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.ACCEPTED).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.REJECTED).build()); // Signature stamps. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.INITIAL_HERE).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.SIGN_HERE).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.WITNESS).build()); // Custom stamp. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.CUSTOM).build()); // Revised/rejected stamps with localized datetime subtext. items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.REVISED).withDateTimeSubtitle(true, true).build()); items.add(StampPickerItem.fromPredefinedType(context, PredefinedStampType.REJECTED).withDateTimeSubtitle(true, true).build());
When a user picks the
PredefinedStampType.CUSTOM
stamp, Nutrient will show a stamp creation dialog that can be used to create custom stamps with user-definable text and colors, and (if desired) the current date.
Image stamp annotations
Nutrient supports stamp annotations generated from a bitmap image. These image stamp annotations cannot have a localized subject, subtext, or text color. Use StampPickerItem#fromBitmap(android.graphics.Bitmap)
to create a builder for bitmap stamp annotations:
val items = ArrayList<StampPickerItem>() ... try { val bitmap = BitmapFactory.decodeStream(getAssets().open("inline-media/images/exampleimage.jpg")) items.add(StampPickerItem.fromBitmap(bitmap) // Specifying only a single size dimension will produce bitmap stamps preserving the original aspect ratio. .withSize(StampPickerItem.DEFAULT_STAMP_ANNOTATION_PDF_WIDTH) .build()) } catch (e: IOException) { e.printStackTrace() }
final List<StampPickerItem> items = new ArrayList<>(); ... try { final Bitmap bitmap = BitmapFactory.decodeStream(getAssets().open("inline-media/images/exampleimage.jpg")); items.add(StampPickerItem.fromBitmap(bitmap) .withSize(StampPickerItem.DEFAULT_STAMP_ANNOTATION_PDF_WIDTH) .build()); } catch (IOException e) { e.printStackTrace(); }
Stamp images are encoded to the JPEG format, which does not allow transparency.
Vector stamp annotations
Nutrient allows you to override the appearance stream of any annotation. This is especially useful for stamp annotations. Unlike bitmap stamp annotations, stamp annotations with custom appearance streams allow transparency and high-resolution zooming.
Here’s how to define a stamp picker item with a custom appearance stream generator set:
val items = ArrayList<StampPickerItem>() ... // Create the appearance stream generator with a PDF containing a vector logo. val appearanceStreamGenerator = AssetAppearanceStreamGenerator("PSPDFKit Logo.pdf") // Create the picker item with a custom subject and custom appearance stream generator set. items.add(StampPickerItem.fromTitle(context, "Custom subject") .withSize(StampPickerItem.DEFAULT_STAMP_ANNOTATION_PDF_WIDTH) .withAppearanceStreamGenerator(appearanceStreamGenerator) .build())
final List<StampPickerItem> items = new ArrayList<>(); ... // Create the appearance stream generator with a PDF containing a vector logo. AssetAppearanceStreamGenerator appearanceStreamGenerator = new AssetAppearanceStreamGenerator("PSPDFKit Logo.pdf"); // Create the picker item with a custom subject and custom appearance stream generator set. items.add(StampPickerItem.fromTitle(context, "Custom subject") .withSize(StampPickerItem.DEFAULT_STAMP_ANNOTATION_PDF_WIDTH) .withAppearanceStreamGenerator(appearanceStreamGenerator) .build());
Appearance streams of created stamps will be saved into the document when saving. However, they will need to be regenerated whenever stamp annotations are modified after the document is reloaded. For this case, we register a global appearance stream generator on a document. This will generate custom appearance streams for stamp annotations based on their subjects:
override fun onDocumentLoaded(document: PdfDocument) { super.onDocumentLoaded(document) // Register the custom stamp appearance stream generator as a global appearance stream generator. val customStampAppearanceStreamGenerator = CustomStampAppearanceStreamGenerator() document.annotationProvider.addAppearanceStreamGenerator(customStampAppearanceStreamGenerator) // Create the appearance stream generator with a PDF containing a vector logo. val appearanceStreamGenerator = AssetAppearanceStreamGenerator("PSPDFKit Logo.pdf") // Register the created appearance stream generator for the custom subject. customStampAppearanceStreamGenerator.addAppearanceStreamGenerator("Custom subject", appearanceStreamGenerator) }
@Override public void onDocumentLoaded(@NonNull PdfDocument document) { super.onDocumentLoaded(document); // Register the custom stamp appearance stream generator as a global appearance stream generator. CustomStampAppearanceStreamGenerator customStampAppearanceStreamGenerator = new CustomStampAppearanceStreamGenerator(); document.getAnnotationProvider().addAppearanceStreamGenerator(customStampAppearanceStreamGenerator); // Create the appearance stream generator with a PDF containing a vector logo. AssetAppearanceStreamGenerator appearanceStreamGenerator = new AssetAppearanceStreamGenerator("PSPDFKit Logo.pdf"); // Register the created appearance stream generator for the custom subject. customStampAppearanceStreamGenerator.addAppearanceStreamGenerator("Custom subject", appearanceStreamGenerator); }
For a comprehensive example, take a look at CustomStampAnnotationsExample
inside the Catalog app, which shows how to create a different set of default stamp annotations.