pdf_stamp_editor 0.6.0 copy "pdf_stamp_editor: ^0.6.0" to clipboard
pdf_stamp_editor: ^0.6.0 copied to clipboard

PDF viewer with stamp overlays and native export layer (Android+iOS supported).

pdf_stamp_editor #

A Flutter package for viewing PDFs with stamp overlays and exporting stamped PDFs on mobile platforms.

Features #

  • 📄 PDF Viewing: Display PDFs using the powerful pdfrx viewer
  • 🖼️ Stamp Placement: Place and position image or text stamps on PDF pages
  • ✏️ Interactive Editing: Drag, resize, rotate, and select stamps with gestures (including cross-page dragging)
  • 💾 PDF Export: Export stamped PDFs with vector-based stamping (mobile only)
  • 🎮 Programmatic Control: Full API for adding, updating, and managing stamps
  • 🌐 Web Support: View and place stamps on web (export disabled)
  • 🎨 Customizable: Support for both PNG image stamps and text stamps with configurable defaults and custom rendering

Platform Support #

  • Mobile (iOS/Android): Full support including export
  • ⚠️ Web: View and place stamps only (export not supported)
  • Desktop (Windows/macOS/Linux): Not currently supported

Getting Started #

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  pdf_stamp_editor: ^0.5.0

Quick Start #

import 'package:flutter/material.dart';
import 'package:pdfrx/pdfrx.dart';
import 'package:pdf_stamp_editor/pdf_stamp_editor.dart';
// For loading files, you might also need:
// import 'package:file_picker/file_picker.dart';
// import 'package:path_provider/path_provider.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // Required if you may touch pdfrx engine / PdfDocument APIs early.
  pdfrxFlutterInitialize();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    // Load PDF bytes from file, network, or assets
    // Example: final pdfBytes = await FilePicker.platform.pickFiles(...);
    // Example: final pdfBytes = await http.get(...).then((r) => r.bodyBytes);

    return MaterialApp(
      home: PdfStampEditorPage(
        pdfBytes: pdfBytes, // Uint8List from file/network/assets
        pngBytes: pngBytes, // Optional: Uint8List PNG image for stamp placement
      ),
    );
  }
}

Usage #

Basic Stamp Placement #

Add stamps by tapping on the PDF. The behavior depends on the mode parameter (default is image):

  • Tap: Places a stamp (image or text) at the tapped location
  • Long Press: Disabled by default (can be enabled via enableLongPress: true)
PdfStampEditorPage(
  pdfBytes: pdfBytes,
  pngBytes: pngBytes, 
  mode: StampEditorMode.image, // Default: image. Options: none, text, image
  onStampsChanged: (stamps) {
    print('Stamps count: ${stamps.length}');
  },
)

Configuration #

Customize stamp behavior using configuration classes:

PdfStampEditorPage(
  pdfBytes: pdfBytes,
  pngBytes: pngBytes,
  
  // Configure text stamps
  textStampConfig: const TextStampConfig(
    text: 'CONFIDENTIAL',
    fontSizePt: 24,
    color: Colors.blue,
    fontWeight: FontWeight.bold,
  ),
  // Or disable text stamps entirely:
  // textStampConfig: const TextStampConfig.disabled(),
  
  // Configure image stamps
  imageStampConfig: const ImageStampConfig(
    widthPt: 200,
    heightPt: null, // null = compute from image aspect ratio
    maintainAspectRatio: true, // Use actual image dimensions
  ),
  // Or use explicit dimensions:
  // imageStampConfig: const ImageStampConfig.explicit(
  //   widthPt: 200,
  //   heightPt: 100,
  // ),
  
  // Configure selection styling
  selectionConfig: const SelectionConfig(
    borderColor: Colors.green,
    borderWidth: 3.0,
  ),
  
  // Configure web PDF viewer source name
  webSourceName: 'my-document.pdf',
)

Configuration Options:

  • mode: Control tap behavior (none, text, or image)
  • enableLongPress: Enable/disable adding text stamps via long-press (default: false)
  • TextStampConfig: Customize text stamp text, font size, color, and weight
  • ImageStampConfig: Configure image stamp dimensions and aspect ratio behavior
    • When maintainAspectRatio: true and heightPt: null, height is automatically computed from image dimensions
  • SelectionConfig: Customize selection border and delete button appearance
  • webSourceName: Set the source name for the web PDF viewer

Interactive Editing #

Enable drag, resize, rotate, and selection gestures:

PdfStampEditorPage(
  pdfBytes: pdfBytes,
  enableDrag: true,      // Enable drag gestures (default: false)
  enableResize: true,    // Enable pinch-to-resize (default: true)
  enableRotate: true,    // Enable rotation gestures (default: true)
  enableSelection: true, // Enable tap-to-select (default: true)
)

Gestures:

  • Drag: Tap and hold a stamp, then drag to move it (requires enableDrag: true and a controller). Stamps can be dragged seamlessly across different PDF pages.
  • Resize: Pinch to zoom on a stamp to resize it
  • Rotate: Use rotation gesture (two fingers) on a stamp to rotate it
  • Select: Tap a stamp to select it (shows border with configurable color and width)
  • Delete: Tap the delete button on selected stamps, use controller buttons, or press Backspace/Delete key

Programmatic Control #

Use PdfStampEditorController for programmatic stamp management:

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:pdf_stamp_editor/pdf_stamp_editor.dart';

class MyEditorPage extends StatefulWidget {
  @override
  State<MyEditorPage> createState() => _MyEditorPageState();
}

class _MyEditorPageState extends State<MyEditorPage> {
  late final PdfStampEditorController controller;
  Uint8List? pngBytes; // Load from file picker, network, or assets

  @override
  void initState() {
    super.initState();
    controller = PdfStampEditorController();
    controller.addListener(() {
      print('Stamps changed: ${controller.stamps.length}');
    });
    // Load PNG bytes (e.g., from file picker or assets)
    // _loadPngBytes();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  void _addStamp() {
    if (pngBytes == null) return; // Ensure PNG is loaded

    final stamp = ImageStamp(
      pageIndex: 0,
      centerXPt: 200.0,
      centerYPt: 300.0,
      rotationDeg: 0.0,
      pngBytes: pngBytes!,
      widthPt: 100.0,
      heightPt: 50.0,
    );
    controller.addStamp(stamp);
  }

  @override
  Widget build(BuildContext context) {
    return PdfStampEditorPage(
      pdfBytes: pdfBytes,
      controller: controller,
      enableDrag: true,
      enableResize: true,
      enableRotate: true,
      enableSelection: true,
    );
  }
}

Controller Methods:

  • addStamp(stamp) - Add a stamp programmatically
  • updateStamp(index, stamp) - Update stamp at index
  • removeStamp(index) - Remove stamp at index
  • clearStamps() - Remove all stamps
  • selectStamp(index, {toggle}) - Select/deselect stamp
  • clearSelection() - Clear all selections
  • deleteSelectedStamps() - Delete all selected stamps
  • isSelected(index) - Check if stamp is selected

Exporting PDFs #

Export stamped PDFs on mobile platforms (Android/iOS):

import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:pdf_stamp_editor/pdf_stamp_editor.dart';

Future<void> exportPdf(Uint8List pdfBytes, List<PdfStamp> stamps) async {
  try {
    final outBytes = await PdfStampEditorExporter.applyStamps(
      inputPdfBytes: pdfBytes,
      stamps: stamps, // List<PdfStamp> from PdfStampEditorPage
    );

    // Save or share outBytes
    final dir = await getApplicationDocumentsDirectory();
    final file = File('${dir.path}/stamped.pdf');
    await file.writeAsBytes(outBytes);
    print('Exported to: ${file.path}');
  } catch (e) {
    print('Export failed: $e');
  }
}

Note: Export requires FFI/PDFium and is only available on mobile platforms. The export functionality uses native implementations:

  • Android: PdfBox-Android library
  • iOS: PDFKit framework

Advanced Features #

Configuration #

Customize default stamp creation and styling behavior:

Text Stamp Configuration

// Customize text stamp defaults
textStampConfig: const TextStampConfig(
  text: 'APPROVED',           // Text to place on long press
  fontSizePt: 18,              // Font size in points
  color: Colors.red,           // Text color
  fontWeight: FontWeight.bold, // Font weight
),

// Disable text stamp creation entirely
textStampConfig: const TextStampConfig.disabled(),

Image Stamp Configuration

// Auto-compute height from image aspect ratio (recommended)
imageStampConfig: const ImageStampConfig(
  widthPt: 200,
  heightPt: null,              // null = compute from image
  maintainAspectRatio: true,   // Use actual image dimensions
),

// Use explicit width and height
imageStampConfig: const ImageStampConfig.explicit(
  widthPt: 200,
  heightPt: 100,
),

// Use default aspect ratio (0.35) when maintainAspectRatio is false
imageStampConfig: const ImageStampConfig(
  widthPt: 200,
  heightPt: null,
  maintainAspectRatio: false, // Use default ratio
),

When maintainAspectRatio is true and heightPt is null, the package automatically decodes the PNG image to get its actual dimensions and computes the height to maintain the aspect ratio. This ensures stamps always display with the correct proportions.

Selection Configuration

selectionConfig: const SelectionConfig(
  borderColor: Colors.blue, // Border color for selected stamps
  borderWidth: 2.0,          // Border width in pixels
  deleteButtonConfig: DeleteButtonConfig(
    backgroundColor: Colors.red,
    iconColor: Colors.white,
    size: 28.0,
    offsetX: 24.0,
    offsetY: -24.0,
  ),
  // Or disable delete button:
  // deleteButtonConfig: DeleteButtonConfig.disabled(),
),

Callbacks #

Monitor stamp changes with callbacks:

PdfStampEditorPage(
  pdfBytes: pdfBytes,
  onStampsChanged: (stamps) {
    // Called when stamps list changes (add/remove)
  },
  onStampSelected: (index, stamp) {
    // Called when a stamp is selected
  },
  onStampUpdated: (index, stamp) {
    // Called when a stamp is updated (drag/resize/rotate)
  },
  onStampDeleted: (indices) {
    // Called when stamps are deleted
  },
  onTapDown: () {
    // Called when tapping to place image stamp
  },
  onLongPressDown: () {
    // Called when long-pressing to place text stamp
  },
  onImageStampPlaced: () {
    // Called after image stamp is placed
  },
)

Custom Stamp Rendering #

Customize stamp appearance with stampBuilder. The builder must return a Widget - if you want default rendering for some types, handle them explicitly or omit the stampBuilder parameter:

import 'dart:math' as math;
import 'package:pdf_stamp_editor/pdf_stamp_editor.dart';
import 'package:pdfrx/pdfrx.dart';

PdfStampEditorPage(
  pdfBytes: pdfBytes,
  stampBuilder: (context, stamp, page, scaledPageSize, position) {
    if (stamp is ImageStamp) {
      // Custom rendering for ImageStamp
      final scale = PdfCoordinateConverter.pageScaleFactors(page, scaledPageSize);
      final wPx = stamp.widthPt * scale.sx;
      final hPx = stamp.heightPt * scale.sy;

      return Container(
        width: wPx,
        height: hPx,
        decoration: BoxDecoration(
          border: Border.all(color: Colors.blue, width: 2),
          boxShadow: [
            BoxShadow(
              color: Colors.black26,
              blurRadius: 4,
              offset: Offset(2, 2),
            ),
          ],
        ),
        child: Transform.rotate(
          angle: stamp.rotationDeg * math.pi / 180,
          child: Image.memory(stamp.pngBytes, fit: BoxFit.fill),
        ),
      );
    } else if (stamp is TextStamp) {
      // Custom rendering for TextStamp
      final scale = PdfCoordinateConverter.pageScaleFactors(page, scaledPageSize);
      final fontPx = stamp.fontSizePt * scale.sy;

      return Transform.rotate(
        angle: stamp.rotationDeg * math.pi / 180,
        child: Text(
          stamp.text,
          style: TextStyle(
            fontSize: fontPx,
            fontWeight: FontWeight.bold,
            color: Colors.blue,
          ),
        ),
      );
    }
    // Handle all stamp types or omit stampBuilder to use defaults
    return const SizedBox.shrink();
  },
)

Coordinate Conversion #

Convert between PDF coordinates and screen coordinates for custom implementations. Note: PdfPoint and PdfPage are from the pdfrx package:

import 'package:flutter/material.dart';
import 'package:pdfrx/pdfrx.dart';
import 'package:pdf_stamp_editor/pdf_stamp_editor.dart';

// Convert screen coordinates to PDF coordinates
PdfPoint pdfPoint = PdfCoordinateConverter.viewerOffsetToPdfPoint(
  page: pdfPage, // PdfPage from pdfrx
  localOffsetTopLeft: Offset(100, 100), // Screen position
  scaledPageSizePx: Size(612, 792), // Displayed page size
);

// Convert PDF coordinates to screen coordinates
Offset screenOffset = PdfCoordinateConverter.pdfPointToViewerOffset(
  page: pdfPage,
  xPt: 100.0, // PDF X coordinate (bottom-left origin)
  yPt: 692.0, // PDF Y coordinate (bottom-left origin)
  scaledPageSizePx: Size(612, 792),
);

// Get scale factors for a page
final scaleFactors = PdfCoordinateConverter.pageScaleFactors(
  pdfPage,
  Size(306, 396), // Scaled page size
);

All methods handle PDF page rotations (0°, 90°, 180°, 270°) correctly.

Example App #

See the example/ directory for a complete working example demonstrating:

  • Basic stamp placement
  • Interactive editing with gestures
  • Programmatic control with controller
  • All callback APIs
  • Custom stamp rendering
  • Coordinate conversion utilities
  • Complete workflow from placement to export

Run the example:

cd example
flutter run

API Reference #

Class Description
PdfStampEditorPage Main widget for PDF stamp editing
PdfStampEditorController Controller for programmatic stamp management
PdfStamp Base class for stamps (sealed)
ImageStamp Image stamp with PNG bytes
TextStamp Text stamp with customizable text
TextStampConfig Configuration for text stamp creation
ImageStampConfig Configuration for image stamp creation
StampEditorMode Interaction mode enum (none, text, image)
SelectionConfig Configuration for selection styling
DeleteButtonConfig Configuration for delete button styling
PdfStampEditorExporter Export engine for applying stamps to PDFs
PdfCoordinateConverter Utilities for coordinate conversion
MatrixCalculator Calculate PDF transformation matrices

For detailed API documentation, see the API reference (available after publishing to pub.dev).

Limitations #

  • Web export: Not supported (requires FFI/PDFium). Consider using a backend service or JavaScript/WASM-based PDF writer for web export.
  • Desktop platforms: Windows/macOS/Linux are not currently supported
  • Concurrent operations: The PDF viewer must be hidden during export to prevent concurrent PDFium calls

Dependencies #

  • pdfrx: PDF viewing and rendering
  • image: PNG image processing
  • file_picker: File selection (optional)
  • path_provider: File system access (optional)
  • path: Path manipulation utilities

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

License #

This project is licensed under the MIT License - see the LICENSE file for details.

Additional Information #

For more information, issues, or feature requests, please visit the GitHub repository.

0
likes
140
points
317
downloads

Publisher

unverified uploader

Weekly Downloads

PDF viewer with stamp overlays and native export layer (Android+iOS supported).

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

file_picker, flutter, image, path, path_provider, pdfrx

More

Packages that depend on pdf_stamp_editor

Packages that implement pdf_stamp_editor