使用C++实现Windows和Linux的Flutter文档扫描插件

文档扫描应用大都是移动应用。结合手机摄像头可以实现文档拍摄,边缘检测,自动剪裁,透视矫正,滤镜等功能。但是桌面办公也少不了文档处理。这里分享下如何使用Dynamsoft Document Normalizer C++ SDK实现用于桌面文档处理的Flutter插件。

Flutter文档扫描插件下载

需要快速体验可以直接访问
https://pub.dev/packages/flutter_document_scan_sdk

生成配置Flutter桌面插件工程

因为C++ SDK只提供了Windows和Linux的库,所以我们创建一个支持Windows和Linux的Flutter插件工程:

flutter create --org com.dynamsoft --template=plugin --platforms=windows,linux .

我们看到Windows和Linux插件代码都是通过CMake编译的。所以需要打开CMakeLists.txt文件配置下编译环境。

Windows

link_directories("${PROJECT_SOURCE_DIR}/lib/") 

target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin "DynamsoftCorex64" "DynamsoftDocumentNormalizerx64")

set(flutter_document_scan_sdk_bundled_libraries
  "${PROJECT_SOURCE_DIR}/bin/"
  PARENT_SCOPE
)

Linux

link_directories("${PROJECT_SOURCE_DIR}/lib/") 

target_link_libraries(${PLUGIN_NAME} PRIVATE flutter "DynamsoftCore" "DynamsoftDocumentNormalizer")

set(flutter_document_scan_sdk_bundled_libraries
  
  PARENT_SCOPE
)

两个平台有一些差异。用于Windows和Linux的库名字有一些不同。Windows上因为链接的是*.lib文件,最后还需要指定*.dll文件的路径用于打包。

Flutter和C++接口实现

Flutter和底层代码是通过method channel通信的。

首先在flutter_document_scan_sdk_method_channel.dart中定义上层接口:

class MethodChannelFlutterDocumentScanSdk
    extends FlutterDocumentScanSdkPlatform {
   
  
  final methodChannel = const MethodChannel('flutter_document_scan_sdk');

  
  Future<String?> getPlatformVersion() async {
   
    final version =
        await methodChannel.invokeMethod<String>('getPlatformVersion');
    return version;
  }

  
  Future<int?> init(String path, String key) async {
   
    return await methodChannel
        .invokeMethod<int>('init', {
   'path': path, 'key': key});
  }

  
  Future<int?> setParameters(String params) async {
   
    return await methodChannel
        .invokeMethod<int>('setParameters', {
   'params': params});
  }

  
  Future<String?> getParameters() async {
   
    return await methodChannel.invokeMethod<String>('getParameters');
  }

  
  Future<List<DocumentResult>> detect(String file) async {
   
    List? results = await methodChannel.invokeListMethod<dynamic>(
      'detect',
      {
   'file': file},
    );

    return _resultWrapper(results);
  }

  List<DocumentResult> _resultWrapper(List<dynamic>? results) {
   
    List<DocumentResult> output = [];

    if (results != null) {
   
      for (var result in results) {
   
        int confidence = result['confidence'];
        List<Offset> offsets = [];
        int x1 = result['x1'];
        int y1 = result['y1'];
        int x2 = result['x2'];
        int y2 = result['y2'];
        int x3 = result['x3'];
        int y3 = result['y3'];
        int x4 = result['x4'];
        int y4 = result['y4'];
        offsets.add(Offset(x1.toDouble(), y1.toDouble()));
        offsets.add(Offset(x2.toDouble(), y2.toDouble()));
        offsets.add(Offset(x3.toDouble(), y3.toDouble()));
        offsets.add(Offset(x4.toDouble(), y4.toDouble()));
        DocumentResult documentResult = DocumentResult(confidence, offsets, []);
        output.add(documentResult);
      }
    }

    return output;
  }

  
  Future<NormalizedImage?> normalize(String file, dynamic points) async {
   
    Offset offset = points[0];
    int x1 = offset.dx.toInt();
    int y1 = offset.dy.toInt();

    offset = points[1];
    int x2 = offset.dx.toInt();
    int y2 = offset.dy.toInt();

    offset = points[2];
    int x3 = offset.dx.toInt();
    int y3 = offset.dy.toInt();

    offset = points[3];
    int x4 = offset.dx.toInt();
    int y4 = offset.dy.toInt();
    Map? result = await methodChannel.invokeMapMethod<String, dynamic>(
      'normalize',
      {
   
        'file': file,
        'x1': x1,
        'y1': y1,
        'x2': x2,
        'y2': y2,
        'x3': x3,
        'y3': y3,
        'x4': x4,
        'y4': y4
      },
    );

    if (result != null) {
   
      return NormalizedImage(
        result['data'],
        result['width'],
        result['height'],
      );
    }

    return null;
  }

  
  Future<int?> save(String filename) async {
   
    return await methodChannel
        .invokeMethod<int>('save', {
   'filename': filename});
  }
}

然后在C++中解析接口名和参数名。Windows上C++的入口函数是FlutterDocumentScanSdkPlugin::HandleMethodCall,而Linux上是flutter_document_scan_sdk_plugin_handle_method_call

Windows

#include "flutter_document_scan_sdk_plugin.h"

#include 

#include 

#include 
#include 
#include 

#include 
#include 

void FlutterDocumentScanSdkPlugin::HandleMethodCall(
      const flutter::MethodCall<flutter::EncodableValue> &method_call,
      std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result){
   }

Linux

#include "include/flutter_document_scan_sdk/flutter_document_scan_sdk_plugin.h"

#include 
#include 
#include 

#include 

static void flutter_document_scan_sdk_plugin_handle_method_call(
    FlutterDocumentScanSdkPlugin 

你可能感兴趣的:(flutter,文档,插件,编程,c++)