我想以电子方式签署 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();
}
}
您可以使用此代码将小部件的偏移量转换为 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));
需要将屏幕上的偏移值转换为实际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);