使用 Flutter 中的 suncfusion pdf 插件对 PDF 进行电子签名

问题描述 投票:0回答:2

我想以电子方式签署 pdf。所以我使用 signature 包来绘制签名,使用 Draggable 小部件通过 Stack 将图像定位在 pdf 上,并使用 syncfusion_flutter_pdf 在 Pdf 上绘制图像。当使用从 Draggable 小部件获得的偏移量绘制图像时,其绘制在不同的位置。我什至尝试将偏移量从本地位置转换为全局位置,但仍然不起作用。

我知道代码很大,但无法避免任何一行。任何帮助都会很棒

import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf_editing/helper/constants.dart';
import 'package:signature/signature.dart';
import 'package:syncfusion_flutter_pdf/pdf.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';

class PdfViewer extends StatefulWidget {
  PdfViewer({this.file});

  File? file;

  @override
  State<PdfViewer> createState() => _PdfViewerState();
}

class _PdfViewerState extends State<PdfViewer> {
  final controller = TextEditingController();
  Offset? _offset;
  bool isFixed = false;
  File? imageFile = File('');
  late File file;
  final _signatureController = SignatureController();
  Uint8List? _signatureBytes;
  int currentPage = 0;

  createFile() async {
    var dir = await getApplicationDocumentsDirectory();
    file = File('${dir.path}/doc.pdf');
    file.writeAsBytes(widget.file!.readAsBytesSync());
    setState(() {});
  }

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

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          actions: [
            InkWell(
                onTap: () {
                  setState(() {
                    isFixed = false;
                  });
                  showSignatureDialogue();
                },
                child: Icon(Icons.edit)),
            sbw(20),
          ],
        ),
        body: SingleChildScrollView(
          child: Column(
            children: [
              PdfWidget(
                  file: file,
                  offset: _offset ?? Offset(37.5.w, 37.5.h),
                  signatureBytes: _signatureBytes,
                  isFixed: isFixed,
                  onDragEnd: (offset) {
                    _offset = offset;
                    setState(() {});
                  }),
            ],
          ),
        ),
      ),
    );
  }

  showSignatureDialogue() {
    showDialog(
        context: context,
        builder: (c) {
          return Dialog(
            insetPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 100),
            child: Container(
              padding: EdgeInsets.all(20),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Signature(
                    controller: _signatureController,
                    width: 300,
                    height: 300,
                    backgroundColor: Colors.yellow.withOpacity(0.2),
                  ),
                  Row(
                    children: [
                      TextButton(
                        onPressed: () {
                          _signatureController.clear();
                        },
                        child: Text('clear'),
                      ),
                      TextButton(
                        onPressed: () async {
                          final exportController = SignatureController(
                            penStrokeWidth: 2,
                            penColor: Colors.black,
                            exportBackgroundColor: Colors.white,
                            points: _signatureController.points,
                          );

                          _signatureBytes = await exportController.toPngBytes();

                          exportController.dispose();
                          setState(() {});
                          Navigator.pop(context);
                        },
                        child: Text('confirm'),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          );
        });
  }
}

class PdfWidget extends StatefulWidget {
  PdfWidget({
    Key? key,
    this.signatureBytes,
    required this.file,
    required this.offset,
    required this.onDragEnd,
    required this.isFixed,
  }) : super(key: key);

  final Uint8List? signatureBytes;
  final File file;
  final Offset offset;
  final Function(Offset) onDragEnd;
  bool isFixed = false;

  @override
  State<PdfWidget> createState() => _PdfWidgetState();
}

class _PdfWidgetState extends State<PdfWidget> {
  int currentPage = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
          height: 70.h,
          child: SfPdfViewer.file(
            widget.file,
            onPageChanged: (page) {
              currentPage = page.newPageNumber;
              setState(() {});
            },
          ),
        ),
        Visibility(
          visible: widget.signatureBytes != null && !widget.isFixed,
          child: Positioned(
            top: widget.offset.dy,
            left: widget.offset.dx,
            child: Draggable(
              childWhenDragging: Container(),
              feedback: Material(
                child: imageWidget(),
              ),
              onDragEnd: (details) {
                RenderBox renderBox = context.findRenderObject() as RenderBox;
                var offset = renderBox.globalToLocal(details.offset);
                widget.onDragEnd(offset);
                setState(() {});
              },
              child: Column(
                children: [
                  imageWidget(),
                  Visibility(
                    visible: !widget.isFixed,
                    child: InkWell(
                        onTap: () {
                          final PdfDocument document = PdfDocument(
                              inputBytes: widget.file.readAsBytesSync());
                          final PdfBitmap image =
                              PdfBitmap(widget.signatureBytes!);
                          RenderBox renderBox =
                              context.findRenderObject() as RenderBox;
                          var offset = renderBox.localToGlobal(
                              Offset(widget.offset.dx, widget.offset.dy));
                          document.pages[currentPage].graphics.drawImage(
                              image,
                              Rect.fromLTWH(offset.dx, offset.dy, 100, 50)
                                  );
                          widget.file.writeAsBytes(document.save());
                          document.dispose();
                          widget.isFixed = true;
                          setState(() {});
                        },
                        child: Icon(Icons.done)),
                  )
                ],
              ),
            ),
          ),
        ),
      ],
    );
  }

  Widget imageWidget() {
    if (widget.signatureBytes != null) {
      return Image.memory(
        widget.signatureBytes!,
        height: 50,
        width: 100,
        fit: BoxFit.contain,
      );
    } else
      return Container();
  }
}

enter image description here

flutter pdf draggable syncfusion electronic-signature
2个回答
1
投票

您可以使用此代码将小部件的偏移量转换为 PDF 页面上相应的偏移量。

     Offset convertOffsetToPdfPage(
     Offset widgetOffset, Size screenSize, Size pdfPageSize) {

 // Get the screen dimensions of the device.
  double screenWidth = screenSize.width;
  double screenHeight = screenSize.height;

 // Map the widget's offset to the screen's top-left corner (0, 0), assuming
// that the top-left corner of the device's screen corresponds to (0, 0)
// on the top-left corner of the page in the PDF.
      double xRatio = widgetOffset.dx / screenWidth;
      double yRatio = widgetOffset.dy / screenHeight;
// Recalculate the widget's offset based on the PDF page size.

   double xOffset = xRatio * pdfPageSize.width;
   double yOffset = yRatio * pdfPageSize.height;
     // Add a margin for error to the calculated offsets for accuracy.
   xOffset += pdfPageSize.width * 0.05; // Added for margin of error
   yOffset += pdfPageSize.height * 0.03; // Added for margin of error

// As a result, the widget's offset is adjusted based on the device's screen size.
 return Offset(xOffset, yOffset);
}

var convertedOffset = convertOffsetToPdfPage(
                          offset,
                          MediaQuery.of(context).size,
                          document.pages[currentPage].size);
               
document.pages[currentPage].graphics.drawImage(
                          image,
                          Rect.fromLTWH(
                              convertedOffset.dx,
                              convertedOffset.dy,
                              document.pages[currentPage].size.height * 0.1,
                              document.pages[currentPage].size.width *
                                  0.15));

0
投票

需要将屏幕上的偏移值转换为实际pdf上的偏移量,因为实际pdf文件是一个A4字,固定宽度:595,高度:842。 首先需要计算要绘制的点相对于正在显示的pdf页面的偏移量,因为它不在位置(0, 0),而是距边框一定距离x(由于syncfusion的pdf显示机制) 。 因为显示的pdf的宽度与实际尺寸595成正比,所以您可以通过简单地用842缩放来完全计算出显示的pdf的高度。 然后减去padding即可得到要绘制的结果:

  Offset convertOffsetToPdfPage( Offset widgetOffset, Size screenSize, Size pdfPageSize) {
double screenWidth = screenSize.width;
double xRatio = (widgetOffset.dx + 25) / screenWidth;
double xOffset = xRatio * pdfPageSize.width;

// tính được height pdf thực tế đang hiển thị trên màn hình
double height_pdf_thucte =
    screenWidth / pdfPageSize.width * pdfPageSize.height;

// tính được phần padding thừa do view pdf nằm trong container
double height_padding_thua = (HEIGHT_CONTAINER_PDF - height_pdf_thucte) / 2;

double yOffset = (widgetOffset.dy - height_padding_thua) /
    height_pdf_thucte *
    pdfPageSize.height;

return Offset(xOffset, yOffset);

} enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.