pdf_to_image_converter 0.0.4 copy "pdf_to_image_converter: ^0.0.4" to clipboard
pdf_to_image_converter: ^0.0.4 copied to clipboard

A comprehensive Flutter package for converting PDF documents to images with quality presets, page rotation, thumbnail generation, batch processing, cancellation support, and metadata extraction. Perfe [...]

example/lib/main.dart

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:pdf_to_image_converter/pdf_to_image_converter.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'PDF to Image Converter - Full Features Demo',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: const PdfConverterDemo(),
    );
  }
}

class PdfConverterDemo extends StatefulWidget {
  const PdfConverterDemo({super.key});

  @override
  State<PdfConverterDemo> createState() => _PdfConverterDemoState();
}

class _PdfConverterDemoState extends State<PdfConverterDemo> {
  final PdfImageConverter _converter = PdfImageConverter();
  Uint8List? _currentImage;
  List<Uint8List?> _thumbnails = [];
  RenderQuality _selectedQuality = RenderQuality.high;
  PageRotation _selectedRotation = PageRotation.rotate0;
  bool _isLoading = false;
  String _statusMessage = '';
  CancellationToken? _cancellationToken;
  int _startPage = 0;
  int _endPage = 0;

  @override
  void dispose() {
    _converter.closePdf();
    super.dispose();
  }

  void _showMessage(String message) {
    setState(() => _statusMessage = message);
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message), duration: const Duration(seconds: 2)),
    );
  }

  Future<void> _pickAndOpenPdf() async {
    try {
      setState(() => _isLoading = true);
      final path = await PdfPicker.pickPdf();

      if (path != null) {
        await _converter.openPdf(path);

        // Display metadata
        final metadata = _converter.metadata;
        _showMessage(
          'PDF Opened: ${metadata?.title ?? "Unknown"}\n'
          'Pages: ${_converter.pageCount}',
        );

        // Reset page range
        setState(() {
          _startPage = 0;
          _endPage = _converter.pageCount - 1;
        });

        // Generate thumbnails for all pages
        await _generateAllThumbnails();

        // Render first page
        await _renderCurrentPage();
      }
    } catch (e) {
      _showMessage('Error opening PDF: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _generateAllThumbnails() async {
    if (!_converter.isOpen) return;

    setState(() {
      _isLoading = true;
      _statusMessage = 'Generating thumbnails...';
    });

    try {
      _cancellationToken = CancellationToken();
      final thumbnails = await _converter.renderAllThumbnails(
        maxWidth: 120,
        maxHeight: 160,
        cancellationToken: _cancellationToken,
        onProgress: (current, total) {
          setState(() {
            _statusMessage = 'Thumbnail: $current/$total';
          });
        },
      );

      setState(() {
        _thumbnails = thumbnails;
        _statusMessage = 'Generated ${thumbnails.length} thumbnails';
      });
    } catch (e) {
      _showMessage('Error generating thumbnails: $e');
    } finally {
      setState(() => _isLoading = false);
      _cancellationToken = null;
    }
  }

  Future<void> _renderCurrentPage() async {
    if (!_converter.isOpen) return;

    setState(() => _isLoading = true);

    try {
      final image = await _converter.renderPage(
        _converter.currentPage,
        quality: _selectedQuality,
        rotation: _selectedRotation,
      );

      setState(() {
        _currentImage = image;
        _statusMessage = 'Page ${_converter.currentPage + 1} rendered';
      });
    } catch (e) {
      _showMessage('Error rendering page: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _renderPageRange() async {
    if (!_converter.isOpen) return;

    setState(() {
      _isLoading = true;
      _statusMessage = 'Rendering page range...';
    });

    try {
      _cancellationToken = CancellationToken();
      final images = await _converter.renderPageRange(
        startPage: _startPage,
        endPage: _endPage,
        quality: _selectedQuality,
        rotation: _selectedRotation,
        cancellationToken: _cancellationToken,
        onProgress: (current, total) {
          setState(() {
            _statusMessage = 'Rendering: $current/$total';
          });
        },
      );

      // Save the rendered range
      final directory = await getApplicationDocumentsDirectory();
      final outputDir = '${directory.path}/pdf_page_range';

      final saved = await _converter.saveAllImages(
        images,
        outputDir,
        fileNamePattern: 'page_{index}.png',
        cancellationToken: _cancellationToken,
        onProgress: (current, total, success) {
          setState(() {
            _statusMessage = 'Saving: $current/$total';
          });
        },
      );

      _showMessage('Saved $saved images to $outputDir');
    } catch (e) {
      _showMessage('Error rendering range: $e');
    } finally {
      setState(() => _isLoading = false);
      _cancellationToken = null;
    }
  }

  Future<void> _renderWithCustomSettings() async {
    if (!_converter.isOpen || _converter.pageCount < 3) {
      _showMessage('Need at least 3 pages for this demo');
      return;
    }

    setState(() {
      _isLoading = true;
      _statusMessage = 'Rendering with custom settings...';
    });

    try {
      _cancellationToken = CancellationToken();

      // Different settings for different pages
      final configs = [
        PageRenderConfig(pageIndex: 0, scale: 4.0, background: Colors.white),
        PageRenderConfig(
          pageIndex: 1,
          scale: 2.0,
          rotation: PageRotation.rotate90,
        ),
        if (_converter.pageCount > 2)
          PageRenderConfig(
            pageIndex: 2,
            scale: 3.0,
            rotation: PageRotation.rotate180,
          ),
      ];

      final images = await _converter.renderPagesWithConfig(
        configs,
        cancellationToken: _cancellationToken,
        onProgress: (current, total) {
          setState(() {
            _statusMessage = 'Custom rendering: $current/$total';
          });
        },
      );

      final directory = await getApplicationDocumentsDirectory();
      final outputDir = '${directory.path}/pdf_custom_settings';

      final saved = await _converter.saveAllImages(
        images,
        outputDir,
        fileNamePattern: 'custom_page_{index}.png',
      );

      _showMessage('Saved $saved images with custom settings to $outputDir');
    } catch (e) {
      _showMessage('Error: $e');
    } finally {
      setState(() => _isLoading = false);
      _cancellationToken = null;
    }
  }

  Future<void> _renderAllPages() async {
    if (!_converter.isOpen) return;

    setState(() {
      _isLoading = true;
      _statusMessage = 'Rendering all pages...';
    });

    try {
      _cancellationToken = CancellationToken();

      final images = await _converter.renderAllPages(
        quality: _selectedQuality,
        rotation: _selectedRotation,
        cancellationToken: _cancellationToken,
        onProgress: (current, total) {
          setState(() {
            _statusMessage = 'Rendering: $current/$total';
          });
        },
      );

      final directory = await getApplicationDocumentsDirectory();
      final outputDir = '${directory.path}/pdf_all_pages';

      final saved = await _converter.saveAllImages(
        images,
        outputDir,
        fileNamePattern: 'page_{index}.png',
        cancellationToken: _cancellationToken,
        onProgress: (current, total, success) {
          setState(() {
            _statusMessage = 'Saving: $current/$total';
          });
        },
      );

      _showMessage('Saved $saved images to $outputDir');
    } catch (e) {
      _showMessage('Error: $e');
    } finally {
      setState(() => _isLoading = false);
      _cancellationToken = null;
    }
  }

  Future<void> _showPageSizes() async {
    if (!_converter.isOpen) return;

    setState(() => _isLoading = true);

    try {
      final sizes = await _converter.getAllPageSizes();

      showDialog(
        // ignore: use_build_context_synchronously
        context: context,
        builder: (context) => AlertDialog(
          title: const Text('Page Sizes'),
          content: SizedBox(
            width: double.maxFinite,
            child: ListView.builder(
              shrinkWrap: true,
              itemCount: sizes.length,
              itemBuilder: (context, index) {
                final size = sizes[index];
                return ListTile(
                  title: Text('Page ${index + 1}'),
                  subtitle: Text(
                    '${size.width.toStringAsFixed(1)} x ${size.height.toStringAsFixed(1)} pts',
                  ),
                );
              },
            ),
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('Close'),
            ),
          ],
        ),
      );
    } catch (e) {
      _showMessage('Error: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  void _cancelOperation() {
    _cancellationToken?.cancel();
    _showMessage('Operation cancelled');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('PDF to Image - All Features'),
        actions: [
          if (_isLoading && _cancellationToken != null)
            IconButton(
              icon: const Icon(Icons.cancel),
              onPressed: _cancelOperation,
              tooltip: 'Cancel Operation',
            ),
        ],
      ),
      body: Column(
        children: [
          // Control Panel
          Card(
            margin: const EdgeInsets.all(8),
            child: Padding(
              padding: const EdgeInsets.all(8),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  // PDF Info
                  if (_converter.isOpen) ...[
                    Text(
                      'PDF: ${_converter.metadata?.title ?? "Unknown"}',
                      style: const TextStyle(fontWeight: FontWeight.bold),
                    ),
                    Text('Pages: ${_converter.pageCount}'),
                    const Divider(),
                  ],

                  // Quality Selector
                  Row(
                    children: [
                      const Text('Quality: '),
                      const SizedBox(width: 8),
                      Expanded(
                        child: DropdownButton<RenderQuality>(
                          value: _selectedQuality,
                          isExpanded: true,
                          items: RenderQuality.values.map((quality) {
                            return DropdownMenuItem(
                              value: quality,
                              child: Text(
                                '${quality.name} (${quality.scale}x)',
                              ),
                            );
                          }).toList(),
                          onChanged: (value) {
                            if (value != null) {
                              setState(() => _selectedQuality = value);
                            }
                          },
                        ),
                      ),
                    ],
                  ),

                  // Rotation Selector
                  Row(
                    children: [
                      const Text('Rotation: '),
                      const SizedBox(width: 8),
                      Expanded(
                        child: DropdownButton<PageRotation>(
                          value: _selectedRotation,
                          isExpanded: true,
                          items: PageRotation.values.map((rotation) {
                            return DropdownMenuItem(
                              value: rotation,
                              child: Text('${rotation.degrees}°'),
                            );
                          }).toList(),
                          onChanged: (value) {
                            if (value != null) {
                              setState(() => _selectedRotation = value);
                              if (_converter.isOpen) _renderCurrentPage();
                            }
                          },
                        ),
                      ),
                    ],
                  ),

                  // Page Range Selector
                  if (_converter.isOpen) ...[
                    const Divider(),
                    const Text('Page Range:'),
                    Row(
                      children: [
                        Expanded(
                          child: TextField(
                            decoration: const InputDecoration(
                              labelText: 'Start',
                            ),
                            keyboardType: TextInputType.number,
                            controller: TextEditingController(
                              text: '${_startPage + 1}',
                            ),
                            onChanged: (value) {
                              final page = int.tryParse(value);
                              if (page != null &&
                                  page > 0 &&
                                  page <= _converter.pageCount) {
                                _startPage = page - 1;
                              }
                            },
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: TextField(
                            decoration: const InputDecoration(labelText: 'End'),
                            keyboardType: TextInputType.number,
                            controller: TextEditingController(
                              text: '${_endPage + 1}',
                            ),
                            onChanged: (value) {
                              final page = int.tryParse(value);
                              if (page != null &&
                                  page > 0 &&
                                  page <= _converter.pageCount) {
                                _endPage = page - 1;
                              }
                            },
                          ),
                        ),
                      ],
                    ),
                  ],

                  const SizedBox(height: 8),

                  // Action Buttons
                  Wrap(
                    spacing: 8,
                    runSpacing: 8,
                    children: [
                      ElevatedButton.icon(
                        onPressed: _isLoading ? null : _pickAndOpenPdf,
                        icon: const Icon(Icons.folder_open),
                        label: const Text('Open PDF'),
                      ),
                      if (_converter.isOpen) ...[
                        ElevatedButton.icon(
                          onPressed: _isLoading ? null : _showPageSizes,
                          icon: const Icon(Icons.straighten),
                          label: const Text('Page Sizes'),
                        ),
                        ElevatedButton.icon(
                          onPressed: _isLoading ? null : _renderPageRange,
                          icon: const Icon(Icons.pages),
                          label: const Text('Render Range'),
                        ),
                        ElevatedButton.icon(
                          onPressed: _isLoading
                              ? null
                              : _renderWithCustomSettings,
                          icon: const Icon(Icons.settings),
                          label: const Text('Custom Settings'),
                        ),
                        ElevatedButton.icon(
                          onPressed: _isLoading ? null : _renderAllPages,
                          icon: const Icon(Icons.select_all),
                          label: const Text('Render All'),
                        ),
                      ],
                    ],
                  ),

                  // Status
                  if (_statusMessage.isNotEmpty) ...[
                    const SizedBox(height: 8),
                    Text(
                      _statusMessage,
                      style: TextStyle(
                        color: Theme.of(context).primaryColor,
                        fontSize: 12,
                      ),
                    ),
                  ],

                  if (_isLoading) const LinearProgressIndicator(),
                ],
              ),
            ),
          ),

          // Thumbnail Strip
          if (_thumbnails.isNotEmpty) ...[
            const Padding(
              padding: EdgeInsets.all(8),
              child: Text(
                'Thumbnails (tap to view):',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
            ),
            SizedBox(
              height: 100,
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemCount: _thumbnails.length,
                itemBuilder: (context, index) {
                  final thumbnail = _thumbnails[index];
                  if (thumbnail == null) return const SizedBox();

                  return GestureDetector(
                    onTap: () async {
                      final image = await _converter.renderPage(
                        index,
                        quality: _selectedQuality,
                        rotation: _selectedRotation,
                      );
                      setState(() => _currentImage = image);
                    },
                    child: Container(
                      margin: const EdgeInsets.symmetric(horizontal: 4),
                      decoration: BoxDecoration(
                        border: Border.all(
                          color: _converter.currentPage == index
                              ? Colors.blue
                              : Colors.grey,
                          width: 2,
                        ),
                      ),
                      child: Column(
                        children: [
                          Expanded(
                            child: Image.memory(thumbnail, fit: BoxFit.contain),
                          ),
                          Text(
                            '${index + 1}',
                            style: const TextStyle(fontSize: 10),
                          ),
                        ],
                      ),
                    ),
                  );
                },
              ),
            ),
          ],

          // Main Image Display
          Expanded(
            child: _currentImage != null
                ? InteractiveViewer(
                    child: Center(child: Image.memory(_currentImage!)),
                  )
                : const Center(child: Text('Open a PDF to begin')),
          ),

          // Navigation
          if (_converter.isOpen)
            Padding(
              padding: const EdgeInsets.all(8),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  IconButton(
                    onPressed: _converter.currentPage > 0 && !_isLoading
                        ? () async {
                            final image = await _converter.renderPage(
                              _converter.currentPage - 1,
                              quality: _selectedQuality,
                              rotation: _selectedRotation,
                            );
                            setState(() => _currentImage = image);
                          }
                        : null,
                    icon: const Icon(Icons.chevron_left),
                  ),
                  Text(
                    'Page ${_converter.currentPage + 1} / ${_converter.pageCount}',
                  ),
                  IconButton(
                    onPressed:
                        _converter.currentPage < _converter.pageCount - 1 &&
                            !_isLoading
                        ? () async {
                            final image = await _converter.renderPage(
                              _converter.currentPage + 1,
                              quality: _selectedQuality,
                              rotation: _selectedRotation,
                            );
                            setState(() => _currentImage = image);
                          }
                        : null,
                    icon: const Icon(Icons.chevron_right),
                  ),
                ],
              ),
            ),
        ],
      ),
    );
  }
}
16
likes
150
points
237
downloads

Publisher

unverified uploader

Weekly Downloads

A comprehensive Flutter package for converting PDF documents to images with quality presets, page rotation, thumbnail generation, batch processing, cancellation support, and metadata extraction. Perfect for PDF viewing and conversion apps.

Repository (GitHub)
View/report issues

Topics

#pdf #image #converter #pdf-to-image #rendering

Documentation

Documentation
API reference

License

BSD-3-Clause (license)

Dependencies

file_picker, flutter, pdf_image_renderer

More

Packages that depend on pdf_to_image_converter