Flutter PDF文档渲染插件flutter_pdfium的使用

发布于 1周前 作者 eggper 来自 Flutter

Flutter PDF文档渲染插件flutter_pdfium的使用

这个项目是一个Flutter插件,提供了PDFium库的绑定。PDFium是一个开源的PDF渲染引擎,被Chromium浏览器所使用。此FFI插件将库的本机绑定传递给Flutter,并支持每个平台动态加载库。需要注意的是,库文件并不包含在包中,而是在构建过程中下载。

注意: MacOS和iOS将在pod install时下载库文件,其他平台则会在构建过程中通过cmake下载相应的库。

所有PDFium头文件中的公共函数在这个库中都被重新映射。这允许库仅绑定所需的实际结构和元素。

使用

首先,你需要在你的Flutter项目中安装该包:flutter pub add flutter_pdfium。安装完成后,你可以在项目中使用该库。二进制文件将在构建过程中下载(可以在相应的CMakeList.txt或podspec文件中查看)。

要使用该库,可以直接使用Pdfium类(如果你希望手动加载库),或者使用提供的createInitializedLibrary()函数,该函数会确定正确的库名称并加载相应的库。库初始化后即可使用。

请注意,在使用完毕后必须释放资源以销毁库。

import 'dart:ffi' as ffi;

import 'package:flutter_pdfium/flutter_pdfium.dart';

// 初始化PDFium库
final pdfium = createInitializedLibrary();

// 加载PDF文档
final doc = pdfium.LoadDocument(
    'file_path'.toNativeUtf8().cast<ffi.Char>(),
    'password'.toNativeUtf8().cast<ffi.Char>(),
);

// 检查是否成功加载文档
if (doc == ffi.nullptr) {
    print('Failed to load document');
    return;
}

// 获取页面数量
final pageCount = pdfium.GetPageCount(doc);

// 关闭文档
pdfium.CloseDocument(doc);

// 销毁库
pdfium.DestroyLibrary();

开发

要克隆并构建此仓库(及其包含的示例),需要本地生成头文件和源代码。对于已部署的包,头文件已经在包中生成并包含在内(在GitHub Actions步骤期间)。

你需要以下工具:

  • clang
  • just
  • Python 3(至少3.12)
  • curl

当你克隆了仓库后,可以运行just configure。这将下载PDFium版本,创建绑定所需的AST,并生成相应的头文件。之后,FFIGEN将运行以生成Dart绑定。完成此步骤后,你可以通过flutter run运行示例项目。与已部署的包一样,有效的库将在构建过程中下载。


示例代码

以下是完整的示例代码,展示了如何使用flutter_pdfium插件来加载和显示PDF文档。

import 'dart:ffi' as ffi;

import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_pdfium/flutter_pdfium.dart';

void main() async {
  runApp(const ExampleApp());
}

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

  [@override](/user/override)
  Widget build(BuildContext context) => MaterialApp(
        title: 'PDFium 示例',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
          useMaterial3: true,
        ),
        home: const Home(),
      );
}

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

  [@override](/user/override)
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late Pdfium pdfium;
  FPDF_DOCUMENT? document;

  [@override](/user/override)
  void initState() {
    pdfium = createInitializedLibrary();
    super.initState();
  }

  [@override](/user/override)
  void dispose() {
    pdfium.DestroyLibrary();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: const Text('Flutter PDFium 示例应用'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                const Icon(Icons.picture_as_pdf, color: Colors.deepOrange),
                const Text('从内存加载PDF'),
                if (document == null)
                  ElevatedButton(
                      onPressed: loadPdf, child: const Text('加载PDF')),
                if (document != null) ...[
                  ElevatedButton(
                      onPressed: unloadPdf, child: const Text('卸载PDF')),
                  Text(
                      '文档有 ${pdfium.GetPageCount(document!)} 页.'),
                  Text('第一页大小为: ${getPageSize()}')
                ],
              ],
            ),
          ),
        ),
      );

  Future<void> loadPdf() async {
    if (document != null) {
      return;
    }

    final bytes = (await rootBundle.load('assets/sample.pdf')).buffer.asUint8List();
    final newDoc = using((arena) {
      final dataPointer = arena<ffi.Uint8>(bytes.length);
      for (var i = 0; i < bytes.length; i++) {
        dataPointer[i] = bytes[i];
      }

      final voidPointer = dataPointer.cast<ffi.Void>();

      return pdfium.LoadMemDocument(
          voidPointer, bytes.length, ''.toNativeUtf8().cast<ffi.Char>());
    }, malloc);

    setState(() {
      document = newDoc;
    });
  }

  void unloadPdf() {
    if (document == null) {
      return;
    }

    pdfium.CloseDocument(document!);
    setState(() {
      document = null;
    });
  }

  Size getPageSize() {
    if (document == null) {
      return Size.zero;
    }

    final page = pdfium.LoadPage(document!, 0);
    final size = using((arena) {
      final sizePointer = arena<FS_SIZEF_>();
      pdfium.GetPageSizeByIndexF(document!, 0, sizePointer);

      return Size(sizePointer.ref.width, sizePointer.ref.height);
    }, malloc);
    pdfium.ClosePage(page);

    return size;
  }
}

更多关于Flutter PDF文档渲染插件flutter_pdfium的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter PDF文档渲染插件flutter_pdfium的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用flutter_pdfium插件来渲染PDF文档的示例代码。flutter_pdfium是一个强大的插件,用于在Flutter应用中显示和操作PDF文档。

首先,确保你的Flutter项目已经创建并配置好了。如果还没有,可以通过以下命令创建一个新的Flutter项目:

flutter create my_pdf_app
cd my_pdf_app

接下来,添加flutter_pdfium依赖到你的pubspec.yaml文件中:

dependencies:
  flutter:
    sdk: flutter
  flutter_pdfium: ^1.0.0  # 请检查最新版本号

然后运行flutter pub get来安装依赖。

接下来是代码实现部分。在lib目录下创建或修改main.dart文件,如下所示:

import 'package:flutter/material.dart';
import 'package:flutter_pdfium/flutter_pdfium.dart';
import 'package:path_provider/path_provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter PDFium Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  PdfiumController? _pdfiumController;
  late PdfiumPage _currentPage;

  @override
  void initState() {
    super.initState();
    _initPdfium();
  }

  Future<void> _initPdfium() async {
    // 获取应用文档目录
    final directory = await getApplicationDocumentsDirectory();
    final path = directory.path + '/sample.pdf';

    // 下载或复制PDF文件到应用文档目录(这里假设你有一个sample.pdf文件)
    // 在实际应用中,你可能需要从网络下载或从资源文件复制
    // 示例代码省略文件复制部分

    // 创建PdfiumController
    _pdfiumController = PdfiumController(
      path: path,
      password: '',  // 如果PDF有密码,可以在这里提供
    );

    // 加载第一页
    _pdfiumController?.openPage(0);
    _currentPage = await _pdfiumController?.getPage(0) ?? PdfiumPage();

    // 监听页面加载完成
    _pdfiumController?.onPageChanged.listen((page) {
      setState(() {
        _currentPage = _pdfiumController?.getPage(page) ?? PdfiumPage();
      });
    });

    // 监听错误
    _pdfiumController?.onError.listen((error) {
      print('Error: $error');
    });

    if (mounted) {
      setState(() {});
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter PDFium Example'),
      ),
      body: Center(
        child: _pdfiumController != null
            ? PdfiumView(
                controller: _pdfiumController!,
                page: _currentPage,
                onTap: (page) {
                  // 点击页面时跳转到指定页面
                  _pdfiumController?.openPage(page);
                },
              )
            : CircularProgressIndicator(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 示例:前进一页
          final currentPage = _pdfiumController?.currentPage ?? 0;
          _pdfiumController?.openPage(currentPage + 1);
        },
        tooltip: 'Next Page',
        child: Icon(Icons.arrow_forward),
      ),
    );
  }

  @override
  void dispose() {
    _pdfiumController?.dispose();
    super.dispose();
  }
}

在这个示例中,我们做了以下几件事:

  1. 初始化PdfiumController:加载PDF文件并初始化控制器。
  2. 监听页面变化:使用onPageChanged监听器来更新当前显示的页面。
  3. 显示PDF页面:使用PdfiumView小部件来显示PDF页面。
  4. 添加浮动按钮:添加了一个浮动按钮来示例如何前进到下一页。

请注意,这个示例假设你有一个名为sample.pdf的文件,并且它已经被复制或下载到了应用的文档目录中。在实际应用中,你可能需要处理文件的下载或复制逻辑。

另外,flutter_pdfium插件的功能非常强大,你可以查阅其官方文档以了解更多高级用法和配置选项。

回到顶部