Compare commits
6 Commits
feat/migr-
...
b76b59f37b
| Author | SHA1 | Date | |
|---|---|---|---|
| b76b59f37b | |||
| 5384a08905 | |||
| dda5ff28e1 | |||
| 043349a47f | |||
| c36f7ccf98 | |||
| cf3e85bcd7 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -369,3 +369,4 @@ FodyWeavers.xsd
|
||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/tekh_softHSM_test.md
|
||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/publish-output
|
||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/tekh_softHSM_test.md
|
||||
/ignore/EnvelopeGenerator.Form
|
||||
|
||||
@@ -11,7 +11,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||
<ProjectGuid>5e0e17c0-ff5a-4246-bf87-1add85376a27</ProjectGuid>
|
||||
<DesktopBuildPackageLocation>M:\App&Service\0 DD - Smart UP\signFLOW\API\net8\$(Version)\EnvelopeGenerator.API.zip</DesktopBuildPackageLocation>
|
||||
<DesktopBuildPackageLocation>M:\App&Service\0 DD - Smart UP\signFLOW\PreRelease\API\net8\$(Version)\EnvelopeGenerator.API.zip</DesktopBuildPackageLocation>
|
||||
<PackageAsSingleFile>true</PackageAsSingleFile>
|
||||
<DeployIisAppPath>EnvelopeGenerator</DeployIisAppPath>
|
||||
<_TargetId>IISWebDeployPackage</_TargetId>
|
||||
|
||||
@@ -7,5 +7,9 @@
|
||||
double X { get; set; }
|
||||
|
||||
double Y { get; set; }
|
||||
|
||||
double Width { get; set; }
|
||||
|
||||
double Height { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net462;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<TargetFrameworks>net462;net8.0-windows</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net462'">
|
||||
@@ -17,9 +17,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||
<PackageReference Include="itext" Version="8.0.5" />
|
||||
<PackageReference Include="itext.bouncy-castle-adapter" Version="8.0.5" />
|
||||
<PackageReference Include="GdPicture" Version="14.4.7" />
|
||||
<PackageReference Include="GdPicture.runtimes.windows" Version="14.4.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using iText.Kernel.Pdf;
|
||||
using iText.Kernel.Pdf.Canvas;
|
||||
using EnvelopeGenerator.Domain.Interfaces;
|
||||
using iText.Kernel.Colors;
|
||||
using DigitalData.Core.Abstractions.Interfaces;
|
||||
using EnvelopeGenerator.Domain.Interfaces;
|
||||
using GdPicture14;
|
||||
|
||||
#if NETFRAMEWORK
|
||||
using System;
|
||||
@@ -15,8 +12,26 @@ namespace EnvelopeGenerator.PdfEditor
|
||||
{
|
||||
public static class Pdf
|
||||
{
|
||||
private const string LicenseKey = "kG1Qf9PwmqgR8aDmIW2zI_ebj48RzqAJegRxcystEmkbTGQqfkNBdFOXIb6C_A00Ra8zZkrHdfjqzOPXK7kgkF2YDhvrqKfqh4WDug2vOt0qO31IommzkANSuLjZ4zmraoubyEVd25rE3veQ2h_j7tGIoH_LyIHmy24GaXsxdG0yCzIBMdiLbMMMDwcPY-809KeZ83Grv76OVhFvcbBWyYc251vou1N-kGg5_ZlHDgfWoY85gTLRxafjD3KS_i9ARW4BMiy36y8n7UP2jN8kGRnW_04ubpFtfjJqvtsrP_J9D0x7bqV8xtVtT5JI6dpKsVTiMgDCrIcoFSo5gCC1fw9oUopX4TDCkBQttO4-WHBlOeq9dG5Yb0otonVmJKaQA2tP6sMR-lZDs3ql_WI9t91yPWgpssrJUxSHDd27_LMTH_owJIqkF3NOJd9mYQuAv22oNKFYbH8e41pVKb8cT33Y9CgcQ_sy6YDA5PTuIRi67mjKge_nD9rd0IN213Ir9M_EFWqg9e4haWzIdHXQUo0md70kVhPX4UIH_BKJnxEEnFfoFRNMh77bB0N4jkcBEHPl-ghOERv8dOztf4vCnNpzzWvcLD2cqWIm6THy8XGGq9h4hp8aEreRleSMwv9QQAC7mjLwhQ1rBYkpUHlpTjhTLnMwHknl6HH0Z6zzmsgkRKVyfquv94Pd7QbQfZrRka0ss_48pf9p8hAywEn81Q==";
|
||||
|
||||
private static bool _initialized = false;
|
||||
private static readonly object _initLock = new object();
|
||||
|
||||
internal static void EnsureInitialized()
|
||||
{
|
||||
if (_initialized) return;
|
||||
lock (_initLock)
|
||||
{
|
||||
if (_initialized) return;
|
||||
var licenseManager = new LicenseManager();
|
||||
licenseManager.RegisterKEY(LicenseKey);
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Pdf<MemoryStream, MemoryStream> FromMemory(byte[] documentBytes)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return new Pdf<MemoryStream, MemoryStream>(new MemoryStream(documentBytes), new MemoryStream());
|
||||
}
|
||||
|
||||
@@ -26,6 +41,7 @@ namespace EnvelopeGenerator.PdfEditor
|
||||
#endif
|
||||
outputStream = null)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return new Pdf<MemoryStream, MemoryStream>(stream, outputStream ?? new MemoryStream(), disposeInputStream: false, disposeOutputStream: outputStream is null);
|
||||
}
|
||||
}
|
||||
@@ -34,115 +50,114 @@ namespace EnvelopeGenerator.PdfEditor
|
||||
where TInputStream : Stream
|
||||
where TOutputStream : Stream
|
||||
{
|
||||
private readonly PdfDocument _doc;
|
||||
private readonly TInputStream _inputStream;
|
||||
private readonly TOutputStream _outputStream;
|
||||
private readonly PdfReader _reader;
|
||||
private readonly PdfWriter _writer;
|
||||
|
||||
private readonly bool _disposeInputStream;
|
||||
private readonly bool _disposeOutputStream;
|
||||
|
||||
// Holds the current PDF bytes; replaced after each Background() call
|
||||
private byte[] _pdfBytes;
|
||||
|
||||
public Pdf(TInputStream inputStream, TOutputStream outputStream, bool disposeInputStream = true, bool disposeOutputStream = true)
|
||||
{
|
||||
_inputStream = inputStream;
|
||||
_outputStream = outputStream;
|
||||
_reader = new PdfReader(inputStream);
|
||||
_writer = new PdfWriter(outputStream);
|
||||
_doc = new PdfDocument(_reader, _writer);
|
||||
_disposeInputStream = disposeInputStream;
|
||||
_disposeOutputStream = disposeOutputStream;
|
||||
_pdfBytes = ReadAllBytes(inputStream);
|
||||
}
|
||||
|
||||
private static byte[] ReadAllBytes(Stream stream)
|
||||
{
|
||||
if (stream is MemoryStream ms)
|
||||
return ms.ToArray();
|
||||
|
||||
using (var buf = new MemoryStream())
|
||||
{
|
||||
stream.CopyTo(buf);
|
||||
return buf.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output stream containing the edited PDF document.
|
||||
/// Flushes the current PDF bytes to the output stream and returns it.
|
||||
/// After calling this, the instance should not be modified further.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Accessing this property will close the underlying <see cref="PdfDocument"/> to ensure
|
||||
/// all changes are flushed to the stream. After accessing this property, the PDF document
|
||||
/// can no longer be modified using this <see cref="Pdf{TInputStream, TOutputStream}"/> instance.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// The <typeparamref name="TOutputStream"/> instance that contains the updated PDF bytes.
|
||||
/// </returns>
|
||||
public TOutputStream ExportStream()
|
||||
{
|
||||
_doc.Close();
|
||||
_outputStream.SetLength(0);
|
||||
_outputStream.Position = 0;
|
||||
_outputStream.Write(_pdfBytes, 0, _pdfBytes.Length);
|
||||
_outputStream.Position = 0;
|
||||
return _outputStream;
|
||||
}
|
||||
|
||||
#region Edit methods
|
||||
public Pdf<TInputStream, TOutputStream> Document(Action<PdfDocument> edit)
|
||||
{
|
||||
edit(_doc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Pdf<TInputStream, TOutputStream> Page(int pageIndex, Action<PdfPage> design)
|
||||
=> Document(doc =>
|
||||
{
|
||||
var page = doc.GetPage(pageIndex);
|
||||
design(doc.GetPage(pageIndex));
|
||||
});
|
||||
|
||||
public Pdf<TInputStream, TOutputStream> Page(Action<PdfPage> design)
|
||||
{
|
||||
for (int i = 1; i <= _doc.GetNumberOfPages(); i++)
|
||||
Page(i, design);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Pdf<TInputStream, TOutputStream> Design(int pageIndex, Action<PdfCanvas> design)
|
||||
=> Document(doc =>
|
||||
{
|
||||
var page = doc.GetPage(pageIndex);
|
||||
var canvas = new PdfCanvas(page);
|
||||
design(canvas);
|
||||
});
|
||||
#endregion
|
||||
|
||||
public Pdf<TInputStream, TOutputStream> Background<TSignature>(IEnumerable<TSignature> signatures, double widthPx = 1.9500000000000002, double heightPx = 2.52)
|
||||
/// <summary>
|
||||
/// Draws the signature field background rectangles onto the PDF pages.
|
||||
///
|
||||
/// Coordinate system: GdPicture SetOrigin(PdfOriginTopLeft) + SetMeasurementUnit(PdfMeasurementUnitInch)
|
||||
/// matches the Form app's GdPicture annotation coordinate system exactly.
|
||||
/// DB values (X, Y in inches from top-left) are used directly — zero conversion.
|
||||
/// </summary>
|
||||
public Pdf<TInputStream, TOutputStream> Background<TSignature>(IEnumerable<TSignature> signatures, double widthInch = 1.9500000000000002, double heightInch = 2.52)
|
||||
where TSignature : IDocReceiverElement
|
||||
{
|
||||
foreach (var signature in signatures)
|
||||
Page(signature.Page, page =>
|
||||
using (var gdpdf = new GdPicturePDF())
|
||||
{
|
||||
GdPictureStatus loadStatus = gdpdf.LoadFromStream(new MemoryStream(_pdfBytes));
|
||||
if (loadStatus != GdPictureStatus.OK)
|
||||
throw new InvalidOperationException("GdPicturePDF.LoadFromStream failed: " + loadStatus);
|
||||
|
||||
// Top-left origin + inch units = same coordinate system as GdPicture Form app annotations.
|
||||
// DB X and Y (inches from top-left) can be used as-is, no conversion needed.
|
||||
gdpdf.SetOrigin(PdfOrigin.PdfOriginTopLeft);
|
||||
gdpdf.SetMeasurementUnit(PdfMeasurementUnit.PdfMeasurementUnitInch);
|
||||
|
||||
// Bottom accent line height: 2.5 PDF points converted to inches (1 inch = 72 pt)
|
||||
const float bottomLineHeightInch = 2.5f / 72.0f;
|
||||
|
||||
foreach (var sig in signatures)
|
||||
{
|
||||
var canvas = new PdfCanvas(page);
|
||||
canvas.SaveState();
|
||||
canvas.ConcatMatrix(1, 0, 0, -1, 0, page.GetPageSize().GetHeight());
|
||||
gdpdf.SelectPage(sig.Page);
|
||||
|
||||
double inchFactor = 72;
|
||||
double magin = .2;
|
||||
double x = (signature.X - .7 - magin) * inchFactor;
|
||||
double y = (signature.Y - .5 - magin) * inchFactor;
|
||||
double width = widthPx * inchFactor;
|
||||
double height = heightPx * inchFactor;
|
||||
// sig.Width/Height come from DB (the actual stored dimensions).
|
||||
// Fall back to the method parameters only if the element has no stored size.
|
||||
float w = (float)widthInch; // sig.Width > 0 ? (float)sig.Width : (float)widthInch;
|
||||
float h = (float)heightInch; // sig.Height > 0 ? (float)sig.Height : (float)heightInch;
|
||||
|
||||
double bottomLineLength = 2.5;
|
||||
// annotation.Left/Top store the CENTER of the bounding box (GdPicture spec).
|
||||
// DrawRectangle expects the TOP-LEFT corner → subtract half dimensions.
|
||||
float x = (float)sig.X;
|
||||
float y = (float)sig.Y;
|
||||
|
||||
// draw background
|
||||
canvas.SetFillColor(new DeviceRgb(222, 220, 215))
|
||||
.Rectangle(x, y, width, height)
|
||||
.Fill();
|
||||
// Background fill — DeviceRgb(222, 220, 215)
|
||||
// DrawRectangle(Left, Top, Width, Height, Fill, Stroke)
|
||||
gdpdf.SetFillColor(222, 220, 215);
|
||||
gdpdf.DrawRectangle(x, y, w, h, true, false);
|
||||
|
||||
// draw bottom line
|
||||
canvas.SetFillColor(new DeviceRgb(204, 202, 198))
|
||||
.Rectangle(x, y + height - bottomLineLength, width, bottomLineLength)
|
||||
.Fill();
|
||||
// Bottom accent line — DeviceRgb(204, 202, 198)
|
||||
gdpdf.SetFillColor(204, 202, 198);
|
||||
gdpdf.DrawRectangle(x, y + h - bottomLineHeightInch, w, bottomLineHeightInch, true, false);
|
||||
}
|
||||
|
||||
canvas.RestoreState();
|
||||
});
|
||||
var outStream = new MemoryStream();
|
||||
GdPictureStatus saveStatus = gdpdf.SaveToStream(outStream);
|
||||
gdpdf.CloseDocument();
|
||||
|
||||
if (saveStatus != GdPictureStatus.OK)
|
||||
throw new InvalidOperationException("GdPicturePDF.SaveToStream failed: " + saveStatus);
|
||||
|
||||
_pdfBytes = outStream.ToArray();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#region Finalizer
|
||||
#region Dispose
|
||||
private bool _disposed = false;
|
||||
|
||||
~Pdf()
|
||||
{
|
||||
// If Dispose is not called, clean up unmanaged resources
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
@@ -154,23 +169,12 @@ namespace EnvelopeGenerator.PdfEditor
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
if (_disposed) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
// Managed resources
|
||||
_doc?.Close();
|
||||
if (_disposeInputStream) _inputStream?.Dispose();
|
||||
if(_disposeOutputStream) _outputStream?.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
// When called by the finalizer, clean up only unmanaged resources
|
||||
// Unmanaged resources such as PdfDocument, PdfReader, and PdfWriter are already IDisposable; we close them here
|
||||
try { _doc?.Close(); } catch { }
|
||||
try { if(_disposeInputStream) _inputStream?.Dispose(); } catch { }
|
||||
try { if (_disposeOutputStream) _outputStream?.Dispose(); } catch { }
|
||||
if (_disposeOutputStream) _outputStream?.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
@@ -178,16 +182,14 @@ namespace EnvelopeGenerator.PdfEditor
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
if (_disposed) return;
|
||||
|
||||
_doc?.Close();
|
||||
if (_disposeInputStream)
|
||||
{
|
||||
if (_inputStream is IAsyncDisposable asyncInput)
|
||||
await asyncInput.DisposeAsync();
|
||||
else
|
||||
_inputStream.Dispose();
|
||||
_inputStream?.Dispose();
|
||||
}
|
||||
|
||||
if (_disposeOutputStream)
|
||||
@@ -203,4 +205,4 @@ namespace EnvelopeGenerator.PdfEditor
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||
<ProjectGuid>fb2d306b-1042-4a70-31ed-f991a1599371</ProjectGuid>
|
||||
<DesktopBuildPackageLocation>M:\App&Service\0 DD - Smart UP\signFLOW\ReceiverUI\net8\$(Version)\EnvelopeGenerator.ReceiverUI.zip</DesktopBuildPackageLocation>
|
||||
<DesktopBuildPackageLocation>M:\App&Service\0 DD - Smart UP\signFLOW\PreRelease\ReceiverUI\net8\$(Version)\EnvelopeGenerator.ReceiverUI.zip</DesktopBuildPackageLocation>
|
||||
<PackageAsSingleFile>true</PackageAsSingleFile>
|
||||
<DeployIisAppPath>EnvelopeGenerator.ReceiverUI</DeployIisAppPath>
|
||||
<_TargetId>IISWebDeployPackage</_TargetId>
|
||||
|
||||
@@ -11,7 +11,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||
<ProjectGuid>5e0e17c0-ff5a-4246-bf87-1add85376a27</ProjectGuid>
|
||||
<DesktopBuildPackageLocation>M:\App&Service\0 DD - Smart UP\signFLOW\API\net8\$(Version)\EnvelopeGenerator.Server.zip</DesktopBuildPackageLocation>
|
||||
<DesktopBuildPackageLocation>M:\App&Service\0 DD - Smart UP\signFLOW\PreRelease\API\net8\$(Version)\EnvelopeGenerator.Server.zip</DesktopBuildPackageLocation>
|
||||
<PackageAsSingleFile>true</PackageAsSingleFile>
|
||||
<DeployIisAppPath>EnvelopeGenerator</DeployIisAppPath>
|
||||
<_TargetId>IISWebDeployPackage</_TargetId>
|
||||
|
||||
@@ -84,7 +84,7 @@ dotnet publish EnvelopeGenerator.Server\EnvelopeGenerator.Server\EnvelopeGenerat
|
||||
| `--runtime win-x64` | Zielplattform: Windows 64-Bit |
|
||||
| `-o .\publish-output` | Ausgabeverzeichnis |
|
||||
|
||||
### Doğrulama nach dem Publish
|
||||
### Überprüfung nach dem Publish
|
||||
|
||||
Nach erfolgreichem Publish folgende Dateien im Ausgabeverzeichnis prüfen:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PackageId>EnvelopeGenerator.Web</PackageId>
|
||||
@@ -2169,6 +2169,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.11" />
|
||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user