我正在尝试编写代码,在护照上盖上系统用户姓名、日期和签名图像(我将其作为 varbinary 存储在 sql 数据库中,并在模型中存储为 byte)。
这是我的代码:
private async void button_ApprovePassport_Click(object sender, EventArgs e)
{
await ProcessPassportPdfAsync();
}
private async Task ProcessPassportPdfAsync()
{
try
{
// Check if a document is selected
if (gridView_PortalDocs.IsSelected())
{
var PortalDoc = gridView_PortalDocs.GetSelectedDataRow<PortalDocumentInfoModel>();
if (PortalDoc != null)
{
EmployeePortalService EPS = new EmployeePortalService();
if (PortalDoc.PortalFileID == null)
{
cGlobal.ShowErrorMessage("PortalFileID is null. Cannot retrieve document.");
return;
}
var Data = await EPS.GetDocument(PortalDoc.PortalFileID);
if (Data != null && !string.IsNullOrEmpty(Data.FileData))
{
// Convert base64 string to byte array
byte[] fileBytes = Convert.FromBase64String(Data.FileData);
// Create a memory stream from the byte array
using (MemoryStream inputStream = new MemoryStream(fileBytes))
{
// Process the PDF (stamp it)
using (MemoryStream processedStream = StampPdf(inputStream))
{
// Save the processed PDF to a temporary file
string tempFilePath = Path.Combine(Path.GetTempPath(), $"StampedPassport_{Guid.NewGuid()}.pdf");
File.WriteAllBytes(tempFilePath, processedStream.ToArray());
// Open the PDF with the default viewer
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(tempFilePath) { UseShellExecute = true });
// Convert the processed stream back to a base64 string
string processedBase64 = Convert.ToBase64String(processedStream.ToArray());
// Using the API for the document reupload here
// Update the PortalFiles table here
MessageBox.Show("Passport PDF processed and approved successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
else
{
cGlobal.ShowErrorMessage("The document data could not be retrieved from the server or is empty.");
}
}
else
{
cGlobal.ShowErrorMessage("The selected document information is invalid.");
}
}
else
{
cGlobal.ShowInfoMessage("Please select a document to process.");
}
}
catch (Exception ex)
{
cGlobal.ShowErrorMessage($"An error occurred while processing the passport PDF: {ex.Message}");
}
}
private MemoryStream StampPdf(MemoryStream inputStream)
{
// Get the user's signature and first name from the controller
// Use Stefi's for testing then switch to cGlobal.SystemUserID
var SystemUserSignature = Controller.GetSystemUserSignature(345);
// Check if the signature was retrieved successfully
if (SystemUserSignature == null || SystemUserSignature.Count == 0)
{
throw new Exception("User signature not found. Please add one in Access Control.");
}
// Retrieve the first user's details (assuming there's at least one entry)
var user = SystemUserSignature[0];
// Ensure we're using UTF-8 encoding (important for PdfSharp)
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// Load the existing document
PdfDocument document = PdfReader.Open(inputStream, PdfDocumentOpenMode.Modify);
// Get the first page (you might want to stamp all pages)
PdfPage page = document.Pages[0];
// Create graphics object for drawing
XGraphics gfx = XGraphics.FromPdfPage(page);
// Create fonts using system fonts
XFont font = new XFont("Arial", 12);
XFont largeFont = new XFont("Arial", 24);
// Create a semi-transparent brush for the background of the stamp
XColor stampColor = XColor.FromArgb(128, 255, 0, 0); // Semi-transparent red
XBrush stampBrush = new XSolidBrush(stampColor);
// Draw a rectangle for the stamp background
gfx.DrawRectangle(stampBrush, 50, 50, 250, 100);
// Add text for the stamp
gfx.DrawString("APPROVED", largeFont, XBrushes.White, new XRect(50, 50, 250, 50), XStringFormats.Center);
// Add approval details
gfx.DrawString($"By: {user.Forename}", font, XBrushes.White, new XRect(60, 100, 230, 25), XStringFormats.TopLeft);
gfx.DrawString($"Date: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", font, XBrushes.White, new XRect(60, 125, 230, 25), XStringFormats.TopLeft);
// If the signature is available, draw it on the PDF
if (user.Signature != null && user.Signature.Length > 0)
{
// Load the signature image from the byte array
using (var signatureImage = new MemoryStream(user.Signature))
{
XImage image = XImage.FromStream(signatureImage);
gfx.DrawImage(image, 50, 150, 150, 50); // Position and size of the signature
}
}
// Save the document to a new MemoryStream
MemoryStream outputStream = new MemoryStream();
document.Save(outputStream, false);
outputStream.Position = 0; // Reset stream position to the beginning
return outputStream;
}
我收到的错误是“处理护照 PDF 时发生错误:无法访问 MemoryStream 的内部缓冲区”。所以我认为你不能同时使用两个 MemoryStream。为了实现这一目标,我有哪些替代方案?
我所尝试的只是通过 MemorySteam 来实现。
编辑:根据一些评论,我尝试了一些东西。错误就在这里:
XImage image = XImage.FromStream(signatureImage);
所以我尝试不显式处理内存流
if (user.Signature != null && user.Signature.Length > 0)
{
var signatureImage = new MemoryStream(user.Signature);
XImage image = XImage.FromStream(signatureImage);
gfx.DrawImage(image, 50, 150, 150, 50); // Position and size of the signature
}
但我似乎仍然遇到同样的错误。
好的,谢谢您的所有评论,我已经解决了。
错误 System.UnauthorizedAccessException:“无法访问 MemoryStream 的内部缓冲区。”发生的原因是 PdfSharp 中的 XImage.FromStream() 尝试访问 MemoryStream 的内部缓冲区,默认情况下该缓冲区不是公共的。 为了解决这个问题,我创建了一个带有可写缓冲区的新 MemoryStream 并将字节数组复制到其中。这允许 XImage.FromStream() 毫无问题地访问流。解决办法如下:
if (user.Signature != null && user.Signature.Length > 0)
{
// Create a writable MemoryStream with a public buffer
MemoryStream signatureImage = new MemoryStream(user.Signature.Length);
signatureImage.Write(user.Signature, 0, user.Signature.Length);
signatureImage.Position = 0; // Reset position to the beginning
// Load the signature image from the writable MemoryStream
XImage image = XImage.FromStream(signatureImage);
gfx.DrawImage(image, 50, 150, 150, 50); // Position and size of the signature
}
这确保 PdfSharp 可以访问内存流的内部缓冲区。
非常感谢所有贡献者!