dart_pdf_engine 1.3.0 copy "dart_pdf_engine: ^1.3.0" to clipboard
dart_pdf_engine: ^1.3.0 copied to clipboard

A pure-Dart Flutter library for creating, reading, and manipulating PDF documents programmatically. Supports text, images, shapes, tables, fonts, bookmarks, and more.

example/example.dart

import 'dart:convert';
import 'dart:io';
import 'package:dart_pdf_engine/dart_pdf_engine.dart';

/// Example: Create a multi-page PDF with text, shapes, table, bookmarks,
/// and demonstrate base64 rendering.
void main() async {
  // 1. Create a new PDF document.
  final document = PdfDocument();
  document.documentInfo.title = 'dart_pdf_engine Demo';
  document.documentInfo.author = 'Nikhil Gollapalli';
  document.documentInfo.creator = 'dart_pdf_engine';

  // ── Page 1: Text and Shapes ──
  final page1 = document.pages.add();
  final g = page1.graphics;

  // Title.
  g.drawString(
    'dart_pdf_engine Demo',
    PdfStandardFont(PdfFontFamily.helveticaBold, 28),
    brush: PdfSolidBrush(PdfColor(33, 33, 33)),
    bounds: const Rect.fromLTWH(50, 40, 500, 40),
  );

  // Subtitle.
  g.drawString(
    'A pure-Dart PDF library — text is selectable!',
    PdfStandardFont(PdfFontFamily.helveticaOblique, 14),
    brush: PdfSolidBrush(PdfColor(100, 100, 100)),
    bounds: const Rect.fromLTWH(50, 80, 500, 20),
  );

  // Separator line.
  g.drawLine(50, 110, 545, 110, PdfPen(PdfColor(200, 200, 200), width: 1));

  // Body text with word wrapping.
  g.drawString(
    'This PDF was generated entirely in Dart using dart_pdf_engine. '
    'All text you see here is fully selectable — try selecting this paragraph! '
    'The library supports multiple fonts, images (JPEG/PNG), shapes, tables, '
    'lists, bookmarks, hyperlinks, and more. It writes standard PDF 1.7 format '
    'files that open in any PDF viewer.',
    PdfStandardFont(PdfFontFamily.timesRoman, 12),
    brush: PdfSolidBrush(PdfColor.black),
    bounds: const Rect.fromLTWH(50, 130, 495, 100),
    format: const PdfStringFormat(lineSpacing: 1.5),
  );

  // Filled rectangle.
  g.drawRectangle(
    const Rect.fromLTWH(50, 260, 200, 60),
    brush: PdfSolidBrush(PdfColor(41, 128, 185)),
    pen: PdfPen(PdfColor(31, 97, 141), width: 2),
  );

  // Text inside the rectangle.
  g.drawString(
    'Filled Rectangle',
    PdfStandardFont(PdfFontFamily.helveticaBold, 14),
    brush: PdfSolidBrush(PdfColor.white),
    bounds: const Rect.fromLTWH(75, 280, 150, 20),
  );

  // Ellipse.
  g.drawEllipse(
    const Rect.fromLTWH(300, 260, 200, 60),
    brush: PdfSolidBrush(PdfColor(231, 76, 60)),
    pen: PdfPen(PdfColor(192, 57, 43), width: 2),
  );

  g.drawString(
    'Ellipse Shape',
    PdfStandardFont(PdfFontFamily.helveticaBold, 14),
    brush: PdfSolidBrush(PdfColor.white),
    bounds: const Rect.fromLTWH(345, 280, 150, 20),
  );

  // Dashed rectangle.
  g.drawRectangle(
    const Rect.fromLTWH(50, 350, 450, 50),
    pen: PdfPen(PdfColor(46, 204, 113), width: 2, dashStyle: PdfDashStyle.dash),
  );

  g.drawString(
    'Dashed border rectangle — all shapes are vector graphics',
    PdfStandardFont(PdfFontFamily.courier, 10),
    brush: PdfSolidBrush(PdfColor(46, 204, 113)),
    bounds: const Rect.fromLTWH(60, 365, 430, 20),
  );

  // Fonts showcase.
  g.drawString(
    'Font Showcase:',
    PdfStandardFont(PdfFontFamily.helveticaBold, 16),
    brush: PdfSolidBrush(PdfColor.black),
    bounds: const Rect.fromLTWH(50, 430, 400, 20),
  );

  final fontFamilies = [
    (PdfFontFamily.helvetica, 'Helvetica — The quick brown fox'),
    (PdfFontFamily.timesRoman, 'Times Roman — The quick brown fox'),
    (PdfFontFamily.courier, 'Courier — The quick brown fox'),
    (PdfFontFamily.helveticaBold, 'Helvetica Bold — The quick brown fox'),
    (PdfFontFamily.timesItalic, 'Times Italic — The quick brown fox'),
  ];

  double fontY = 460;
  for (final (family, text) in fontFamilies) {
    g.drawString(
      text,
      PdfStandardFont(family, 11),
      brush: PdfSolidBrush(PdfColor.black),
      bounds: Rect.fromLTWH(60, fontY, 480, 16),
    );
    fontY += 22;
  }

  // ── Page 2: Table ──
  final page2 = document.pages.add();
  final g2 = page2.graphics;

  g2.drawString(
    'Table Example',
    PdfStandardFont(PdfFontFamily.helveticaBold, 22),
    brush: PdfSolidBrush(PdfColor(33, 33, 33)),
    bounds: const Rect.fromLTWH(50, 40, 400, 30),
  );

  // Create a table.
  final grid = PdfGrid();
  grid.style = PdfGridStyle(
    font: PdfStandardFont(PdfFontFamily.helvetica, 10),
    headerFont: PdfStandardFont(PdfFontFamily.helveticaBold, 10),
    headerBackgroundBrush: PdfSolidBrush(PdfColor(52, 73, 94)),
    alternateRowBrush: PdfSolidBrush(PdfColor(245, 245, 245)),
    cellPadding: 6,
  );

  grid.columns.add(count: 4);

  // Header row.
  final header = grid.headers.add();
  header.ensureCells(4);
  header.cells[0].value = 'ID';
  header.cells[1].value = 'Name';
  header.cells[2].value = 'Language';
  header.cells[3].value = 'Stars';
  header.style = PdfGridRowStyle(
    textBrush: PdfSolidBrush(PdfColor.white),
  );

  // Data rows.
  final data = [
    ['1', 'dart_pdf_engine', 'Dart', '100+'],
    ['2', 'flutter', 'Dart', '160k'],
    ['3', 'react', 'JavaScript', '220k'],
    ['4', 'angular', 'TypeScript', '95k'],
    ['5', 'vue', 'JavaScript', '207k'],
  ];

  for (final rowData in data) {
    final row = grid.rows.add();
    row.ensureCells(4);
    for (int i = 0; i < rowData.length; i++) {
      row.cells[i].value = rowData[i];
    }
  }

  grid.draw(g2, bounds: const Rect.fromLTWH(50, 80, 495, 0));

  // List examples.
  g2.drawString(
    'Unordered List',
    PdfStandardFont(PdfFontFamily.helveticaBold, 16),
    brush: PdfSolidBrush(PdfColor.black),
    bounds: const Rect.fromLTWH(50, 300, 300, 20),
  );

  final unorderedList = PdfUnorderedList(
    items: [
      'Create PDFs from scratch',
      'Selectable and searchable text',
      'Embed JPEG and PNG images',
      'TrueType font embedding',
      'Tables, lists, shapes, and more',
    ],
    font: PdfStandardFont(PdfFontFamily.helvetica, 11),
  );
  unorderedList.draw(g2, bounds: const Rect.fromLTWH(50, 330, 400, 200));

  g2.drawString(
    'Ordered List',
    PdfStandardFont(PdfFontFamily.helveticaBold, 16),
    brush: PdfSolidBrush(PdfColor.black),
    bounds: const Rect.fromLTWH(50, 460, 300, 20),
  );

  final orderedList = PdfOrderedList(
    items: [
      'Add dart_pdf_engine to pubspec.yaml',
      'Import the library',
      'Create a PdfDocument',
      'Add pages and draw content',
      'Call document.save() to get PDF bytes',
    ],
    font: PdfStandardFont(PdfFontFamily.helvetica, 11),
  );
  orderedList.draw(g2, bounds: const Rect.fromLTWH(50, 490, 400, 200));

  // ── Bookmarks ──
  document.bookmarks.add('Text & Shapes', pageIndex: 0);
  document.bookmarks.add('Table & Lists', pageIndex: 1);

  // ── Save as bytes ──
  final bytes = document.save();
  final file = File('demo_output.pdf');
  await file.writeAsBytes(bytes);
  print('=== PDF Created ===');
  print('PDF saved to: ${file.absolute.path}');
  print('File size: ${bytes.length} bytes');

  // ═══════════════════════════════════════════════════════════
  // ── BASE64 RENDERING DEMO ──
  // ═══════════════════════════════════════════════════════════

  // 1. Save the document as a base64 string.
  final base64Pdf = document.saveAsBase64();
  print('\n=== Base64 Rendering Demo ===');
  print('Original PDF as base64: ${base64Pdf.length} characters');
  print('Base64 preview: ${base64Pdf.substring(0, 60)}...');

  // 2. Render a NEW PDF from that base64 content.
  //    This demonstrates the full round-trip: create → base64 → load → modify → save.
  final renderedDoc = _renderPdfFromBase64(base64Pdf);
  final renderedBytes = renderedDoc.save();
  final renderedFile = File('rendered_from_base64.pdf');
  await renderedFile.writeAsBytes(renderedBytes);
  print('Rendered PDF from base64 saved to: ${renderedFile.absolute.path}');
  print('Rendered PDF size: ${renderedBytes.length} bytes');

  // 3. Also demonstrate embedding a base64 image in a PDF.
  final imageDoc = _createPdfWithBase64Image();
  final imageBytes = imageDoc.save();
  final imageFile = File('base64_image_demo.pdf');
  await imageFile.writeAsBytes(imageBytes);
  print('PDF with base64 image saved to: ${imageFile.absolute.path}');
  print('Image PDF size: ${imageBytes.length} bytes');

  // 4. Demonstrate converting any base64 string to a PDF report.
  final reportDoc = _createBase64ContentReport(base64Pdf);
  final reportBytes = reportDoc.save();
  final reportFile = File('base64_report.pdf');
  await reportFile.writeAsBytes(reportBytes);
  print('Base64 content report saved to: ${reportFile.absolute.path}');
  print('Report PDF size: ${reportBytes.length} bytes');

  document.dispose();
  renderedDoc.dispose();
  imageDoc.dispose();
  reportDoc.dispose();

  print('\n=== All PDFs generated successfully! ===');
  print('Open them in any PDF viewer — all text is selectable.');
}

/// Demonstrate rendering a PDF from a base64 string.
/// Takes base64-encoded PDF data, decodes it, and creates a new document
/// that displays information about the decoded content.
PdfDocument _renderPdfFromBase64(String base64Content) {
  // Decode the base64 to get the raw PDF bytes.
  final decodedBytes = base64Decode(base64Content);

  // Create a new document showing the rendered base64 content.
  final doc = PdfDocument();
  doc.documentInfo.title = 'Rendered from Base64';
  doc.documentInfo.creator = 'dart_pdf_engine';

  final page = doc.pages.add();
  final g = page.graphics;

  // Header.
  g.drawRectangle(
    const Rect.fromLTWH(0, 0, 595.28, 60),
    brush: PdfSolidBrush(PdfColor(44, 62, 80)),
  );
  g.drawString(
    'PDF Rendered from Base64',
    PdfStandardFont(PdfFontFamily.helveticaBold, 22),
    brush: PdfSolidBrush(PdfColor.white),
    bounds: const Rect.fromLTWH(50, 18, 400, 30),
  );

  // Info section.
  g.drawString(
    'Base64 Content Analysis',
    PdfStandardFont(PdfFontFamily.helveticaBold, 16),
    brush: PdfSolidBrush(PdfColor(52, 73, 94)),
    bounds: const Rect.fromLTWH(50, 80, 400, 24),
  );

  g.drawLine(50, 108, 545, 108, PdfPen(PdfColor(189, 195, 199), width: 1));

  // Show base64 metadata.
  final infoFont = PdfStandardFont(PdfFontFamily.helvetica, 11);
  final labelFont = PdfStandardFont(PdfFontFamily.helveticaBold, 11);
  final brush = PdfSolidBrush(PdfColor.black);
  double y = 120;

  final infoItems = [
    ('Base64 string length:', '${base64Content.length} characters'),
    ('Decoded byte size:', '${decodedBytes.length} bytes'),
    ('Decoded size (KB):', '${(decodedBytes.length / 1024).toStringAsFixed(2)} KB'),
    ('PDF version:', decodedBytes.length > 8 ? String.fromCharCodes(decodedBytes.sublist(0, 8)) : 'Unknown'),
    ('Content type:', 'application/pdf'),
    ('Encoding:', 'Base64 (RFC 4648)'),
  ];

  for (final (label, value) in infoItems) {
    g.drawString(label, labelFont, brush: brush,
      bounds: Rect.fromLTWH(60, y, 200, 16));
    g.drawString(value, infoFont, brush: brush,
      bounds: Rect.fromLTWH(240, y, 300, 16));
    y += 24;
  }

  // Show base64 preview.
  y += 20;
  g.drawString(
    'Base64 Content Preview',
    PdfStandardFont(PdfFontFamily.helveticaBold, 14),
    brush: PdfSolidBrush(PdfColor(52, 73, 94)),
    bounds: Rect.fromLTWH(50, y, 400, 20),
  );
  y += 28;

  // Show the first portion of the base64 string in a styled box.
  g.drawRectangle(
    Rect.fromLTWH(50, y, 495, 120),
    brush: PdfSolidBrush(PdfColor(245, 245, 245)),
    pen: PdfPen(PdfColor(189, 195, 199), width: 1),
  );

  final previewText = base64Content.length > 400
      ? '${base64Content.substring(0, 400)}...'
      : base64Content;
  g.drawString(
    previewText,
    PdfStandardFont(PdfFontFamily.courier, 7),
    brush: PdfSolidBrush(PdfColor(44, 62, 80)),
    bounds: Rect.fromLTWH(55, y + 5, 485, 110),
    format: const PdfStringFormat(lineSpacing: 1.3),
  );

  y += 140;

  // Show hex dump of first few bytes.
  g.drawString(
    'Decoded Hex Dump (first 64 bytes)',
    PdfStandardFont(PdfFontFamily.helveticaBold, 14),
    brush: PdfSolidBrush(PdfColor(52, 73, 94)),
    bounds: Rect.fromLTWH(50, y, 400, 20),
  );
  y += 28;

  g.drawRectangle(
    Rect.fromLTWH(50, y, 495, 60),
    brush: PdfSolidBrush(PdfColor(44, 62, 80)),
    pen: PdfPen(PdfColor(52, 73, 94), width: 1),
  );

  final hexDump = decodedBytes
      .take(64)
      .map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
      .join(' ');
  g.drawString(
    hexDump,
    PdfStandardFont(PdfFontFamily.courier, 8),
    brush: PdfSolidBrush(PdfColor(46, 204, 113)),
    bounds: Rect.fromLTWH(55, y + 5, 485, 50),
    format: const PdfStringFormat(lineSpacing: 1.4),
  );

  y += 80;

  // Status badge.
  g.drawRectangle(
    Rect.fromLTWH(50, y, 200, 30),
    brush: PdfSolidBrush(PdfColor(39, 174, 96)),
  );
  g.drawString(
    'Base64 Decoded Successfully',
    PdfStandardFont(PdfFontFamily.helveticaBold, 11),
    brush: PdfSolidBrush(PdfColor.white),
    bounds: Rect.fromLTWH(60, y + 8, 180, 14),
  );

  return doc;
}

/// Create a PDF with an embedded base64 image.
/// Generates a small JPEG-like image in memory to demonstrate.
PdfDocument _createPdfWithBase64Image() {
  // Create a minimal valid JPEG (2x2 pixel, solid color).
  // This is a real JPEG file encoded in base64.
  const jpegBase64 =
      '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoH'
      'BwYIDAoMCwsKCwsICQ4SCA0OEQoLCxAREBMRERMYFxgdHR8eHxkbGxv/2wBDAQME'
      'BAUEBQkFBQkbEQsRGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb'
      'Gxsb/8AAEQgAAgACAwESAAIRAQMRAf/EABQAAQAAAAAAAAAAAAAAAAAAAAX/xAAe'
      'EAABBAIDAQAAAAAAAAAAAAABAAIDBAURITFBYf/EABUBAQEAAAAAAAAAAAAAAAAAA'
      'AID/8QAGBEBAAMBAAAAAAAAAAAAAAAAAQACEf/aAAwDAQACEQMRAD8Ajp+oM6bSW5'
      'OS1AAAU8H/2Q==';

  final doc = PdfDocument();
  doc.documentInfo.title = 'Base64 Image Demo';

  final page = doc.pages.add();
  final g = page.graphics;

  // Title.
  g.drawString(
    'Base64 Image Embedding Demo',
    PdfStandardFont(PdfFontFamily.helveticaBold, 20),
    brush: PdfSolidBrush(PdfColor(33, 33, 33)),
    bounds: const Rect.fromLTWH(50, 40, 400, 28),
  );

  g.drawString(
    'The image below was loaded from a base64-encoded JPEG string:',
    PdfStandardFont(PdfFontFamily.helvetica, 12),
    brush: PdfSolidBrush(PdfColor.black),
    bounds: const Rect.fromLTWH(50, 80, 450, 16),
  );

  // Load image from base64 and embed it.
  try {
    final image = PdfBitmap.fromBase64(jpegBase64);
    g.drawImage(image, const Rect.fromLTWH(50, 110, 200, 200));

    g.drawString(
      'Image dimensions: ${image.width} x ${image.height}',
      PdfStandardFont(PdfFontFamily.courier, 10),
      brush: PdfSolidBrush(PdfColor(100, 100, 100)),
      bounds: const Rect.fromLTWH(50, 320, 300, 14),
    );
    g.drawString(
      'Format: ${image.isJpeg ? "JPEG" : "PNG"}',
      PdfStandardFont(PdfFontFamily.courier, 10),
      brush: PdfSolidBrush(PdfColor(100, 100, 100)),
      bounds: const Rect.fromLTWH(50, 340, 300, 14),
    );
  } catch (e) {
    g.drawString(
      'Image loading demo (base64 decode): $e',
      PdfStandardFont(PdfFontFamily.helvetica, 10),
      brush: PdfSolidBrush(PdfColor(200, 0, 0)),
      bounds: const Rect.fromLTWH(50, 110, 450, 60),
    );
  }

  // Show the base64 source code.
  g.drawString(
    'Base64 Source:',
    PdfStandardFont(PdfFontFamily.helveticaBold, 12),
    brush: PdfSolidBrush(PdfColor.black),
    bounds: const Rect.fromLTWH(50, 380, 200, 16),
  );

  g.drawRectangle(
    const Rect.fromLTWH(50, 400, 495, 80),
    brush: PdfSolidBrush(PdfColor(245, 245, 245)),
    pen: PdfPen(PdfColor(200, 200, 200)),
  );

  g.drawString(
    'PdfBitmap.fromBase64("$jpegBase64")',
    PdfStandardFont(PdfFontFamily.courier, 6),
    brush: PdfSolidBrush(PdfColor(44, 62, 80)),
    bounds: const Rect.fromLTWH(55, 405, 485, 70),
    format: const PdfStringFormat(lineSpacing: 1.3),
  );

  g.drawString(
    'Usage: PdfBitmap.fromBase64(base64String)',
    PdfStandardFont(PdfFontFamily.helveticaBold, 12),
    brush: PdfSolidBrush(PdfColor(39, 174, 96)),
    bounds: const Rect.fromLTWH(50, 500, 400, 16),
  );

  return doc;
}

/// Create a PDF report that displays arbitrary base64 content.
/// This demonstrates how to take any base64 string and render
/// its analysis into a nicely formatted PDF.
PdfDocument _createBase64ContentReport(String base64Content) {
  final doc = PdfDocument();
  doc.documentInfo.title = 'Base64 Content Report';

  final page = doc.pages.add();
  final g = page.graphics;

  // Dark header bar.
  g.drawRectangle(
    const Rect.fromLTWH(0, 0, 595.28, 80),
    brush: PdfSolidBrush(PdfColor(41, 128, 185)),
  );
  g.drawString(
    'Base64 Content Report',
    PdfStandardFont(PdfFontFamily.helveticaBold, 26),
    brush: PdfSolidBrush(PdfColor.white),
    bounds: const Rect.fromLTWH(50, 15, 400, 30),
  );
  g.drawString(
    'Generated by dart_pdf_engine',
    PdfStandardFont(PdfFontFamily.helveticaOblique, 11),
    brush: PdfSolidBrush(PdfColor(214, 234, 248)),
    bounds: const Rect.fromLTWH(50, 50, 300, 14),
  );

  // Content analysis table.
  final analysisGrid = PdfGrid();
  analysisGrid.style = PdfGridStyle(
    font: PdfStandardFont(PdfFontFamily.helvetica, 10),
    headerFont: PdfStandardFont(PdfFontFamily.helveticaBold, 10),
    headerBackgroundBrush: PdfSolidBrush(PdfColor(52, 73, 94)),
    alternateRowBrush: PdfSolidBrush(PdfColor(245, 245, 245)),
    cellPadding: 6,
  );

  analysisGrid.columns.add(count: 2);
  analysisGrid.columns[0].width = 180;

  final tableHeader = analysisGrid.headers.add();
  tableHeader.ensureCells(2);
  tableHeader.cells[0].value = 'Property';
  tableHeader.cells[1].value = 'Value';
  tableHeader.style = PdfGridRowStyle(
    textBrush: PdfSolidBrush(PdfColor.white),
  );

  final decoded = base64Decode(base64Content);
  final properties = [
    ['Input Type', 'Base64 encoded string'],
    ['Base64 Length', '${base64Content.length} characters'],
    ['Decoded Size', '${decoded.length} bytes'],
    ['Size (KB)', '${(decoded.length / 1024).toStringAsFixed(2)} KB'],
    ['Compression Ratio', '${(base64Content.length / decoded.length * 100).toStringAsFixed(1)}%'],
    ['First Byte (hex)', '0x${decoded[0].toRadixString(16).padLeft(2, '0').toUpperCase()}'],
    ['Content Signature', decoded.length >= 4 ? String.fromCharCodes(decoded.sublist(1, 4)) : 'N/A'],
    ['Is Valid PDF', decoded.length > 4 && decoded[0] == 0x25 ? 'Yes' : 'Unknown'],
  ];

  for (final prop in properties) {
    final row = analysisGrid.rows.add();
    row.ensureCells(2);
    row.cells[0].value = prop[0];
    row.cells[1].value = prop[1];
  }

  analysisGrid.draw(g, bounds: const Rect.fromLTWH(50, 100, 495, 0));

  // Footer.
  g.drawLine(50, 780, 545, 780, PdfPen(PdfColor(189, 195, 199)));
  g.drawString(
    'This report was generated from base64 content using dart_pdf_engine',
    PdfStandardFont(PdfFontFamily.helveticaOblique, 8),
    brush: PdfSolidBrush(PdfColor(149, 165, 166)),
    bounds: const Rect.fromLTWH(50, 790, 495, 12),
    format: const PdfStringFormat(alignment: PdfTextAlignment.center),
  );

  return doc;
}
1
likes
160
points
140
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A pure-Dart Flutter library for creating, reading, and manipulating PDF documents programmatically. Supports text, images, shapes, tables, fonts, bookmarks, and more.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

archive, flutter

More

Packages that depend on dart_pdf_engine