Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Incremental saving #112

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageVersion Include="GitVersion.MsBuild" Version="5.12.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="System.Drawing.Common" Version="4.7.3" />
<PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />
<PackageVersion Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageVersion Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
<PackageVersion Include="System.Diagnostics.FileVersionInfo" Version="4.3.0" />
Expand Down Expand Up @@ -99,6 +100,9 @@
<PackageVersion Include="coverlet.collector" Version="1.3.0" />
<PackageVersion Include="Moq" Version="4.16.1" />
<PackageVersion Include="NCrunch.Framework" Version="4.7.0.4" />
<!-- Signature packages -->
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="8.0.0" />
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.2.1" />
<!-- Other packages -->
<!--<PackageVersion Include="Dapper" Version="2.0.123" />-->
<PackageVersion Include="System.IO.Abstractions" Version="13.2.47" />
Expand Down
18 changes: 16 additions & 2 deletions src/foundation/src/PDFsharp/src/PdfSharp-gdi/PdfSharp-gdi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,16 @@
<Compile Include="..\PdfSharp\Pdf.Security\PdfSecurityHandler.cs" Link="Pdf.Security\PdfSecurityHandler.cs" />
<Compile Include="..\PdfSharp\Pdf.Security\PdfSecuritySettings.cs" Link="Pdf.Security\PdfSecuritySettings.cs" />
<Compile Include="..\PdfSharp\Pdf.Security\PdfStandardSecurityHandler.cs" Link="Pdf.Security\PdfStandardSecurityHandler.cs" />
<Compile Include="..\PdfSharp\Pdf.Structure\PdfAttributesBase.cs" Link="Pdf.Structure\PdfAttributesBase.cs" />

<Compile Include="..\PdfSharp\Pdf.Signatures\DefaultSignatureRenderer.cs" Link="Pdf.Signatures\DefaultSignatureRenderer.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\DefaultSigner.cs" Link="Pdf.Signatures\DefaultSigner.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\ISignatureRenderer.cs" Link="Pdf.Signatures\ISignatureRenderer.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\ISigner.cs" Link="Pdf.Signatures\ISigner.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\PdfSignatureOptions.cs" Link="Pdf.Signatures\PdfSignatureOptions.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\PdfSignatureValue.cs" Link="Pdf.Signatures\PdfSignatureValue.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\PdfSigner.cs" Link="Pdf.Signatures\PdfSigner.cs" />

<Compile Include="..\PdfSharp\Pdf.Structure\PdfAttributesBase.cs" Link="Pdf.Structure\PdfAttributesBase.cs" />
<Compile Include="..\PdfSharp\Pdf.Structure\PdfLayoutAttributes.cs" Link="Pdf.Structure\PdfLayoutAttributes.cs" />
<Compile Include="..\PdfSharp\Pdf.Structure\PdfMarkedContentReference.cs" Link="Pdf.Structure\PdfMarkedContentReference.cs" />
<Compile Include="..\PdfSharp\Pdf.Structure\PdfMarkInformation.cs" Link="Pdf.Structure\PdfMarkInformation.cs" />
Expand Down Expand Up @@ -419,7 +428,12 @@
<ProjectReference Include="..\..\..\shared\src\PdfSharp.System\PdfSharp.System.csproj" />
</ItemGroup>

<ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1" />
<PackageReference Include="System.Security.Cryptography.Pkcs" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="PDFsharp.Tests-gdi, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb" />
<InternalsVisibleTo Include="PDFsharp.TestHelper-gdi, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb" />
</ItemGroup>
Expand Down
18 changes: 14 additions & 4 deletions src/foundation/src/PDFsharp/src/PdfSharp-wpf/PdfSharp-wpf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,16 @@
<Compile Include="..\PdfSharp\Pdf.Security\PdfSecurityHandler.cs" Link="Pdf.Security\PdfSecurityHandler.cs" />
<Compile Include="..\PdfSharp\Pdf.Security\PdfSecuritySettings.cs" Link="Pdf.Security\PdfSecuritySettings.cs" />
<Compile Include="..\PdfSharp\Pdf.Security\PdfStandardSecurityHandler.cs" Link="Pdf.Security\PdfStandardSecurityHandler.cs" />
<Compile Include="..\PdfSharp\Pdf.Structure\PdfAttributesBase.cs" Link="Pdf.Structure\PdfAttributesBase.cs" />

<Compile Include="..\PdfSharp\Pdf.Signatures\DefaultSignatureRenderer.cs" Link="Pdf.Signatures\DefaultSignatureRenderer.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\DefaultSigner.cs" Link="Pdf.Signatures\DefaultSigner.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\ISignatureRenderer.cs" Link="Pdf.Signatures\ISignatureRenderer.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\ISigner.cs" Link="Pdf.Signatures\ISigner.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\PdfSignatureOptions.cs" Link="Pdf.Signatures\PdfSignatureOptions.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\PdfSignatureValue.cs" Link="Pdf.Signatures\PdfSignatureValue.cs" />
<Compile Include="..\PdfSharp\Pdf.Signatures\PdfSigner.cs" Link="Pdf.Signatures\PdfSigner.cs" />

<Compile Include="..\PdfSharp\Pdf.Structure\PdfAttributesBase.cs" Link="Pdf.Structure\PdfAttributesBase.cs" />
<Compile Include="..\PdfSharp\Pdf.Structure\PdfLayoutAttributes.cs" Link="Pdf.Structure\PdfLayoutAttributes.cs" />
<Compile Include="..\PdfSharp\Pdf.Structure\PdfMarkedContentReference.cs" Link="Pdf.Structure\PdfMarkedContentReference.cs" />
<Compile Include="..\PdfSharp\Pdf.Structure\PdfMarkInformation.cs" Link="Pdf.Structure\PdfMarkInformation.cs" />
Expand Down Expand Up @@ -426,8 +435,9 @@
<InternalsVisibleTo Include="PDFsharp.TestHelper-wpf, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb" />
</ItemGroup>

<!--<ItemGroup>
<PackageReference Include="System.Reflection.MetadataLoadContext" />
</ItemGroup>-->
<ItemGroup>
<PackageReference Include="System.Formats.Asn1" />
<PackageReference Include="System.Security.Cryptography.Pkcs" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public string Name
string name = Elements.GetString(Keys.T);
return name;
}
set
{
Elements.SetString(Keys.T, value);
}
}

/// <summary>
Expand Down Expand Up @@ -267,6 +271,10 @@ public PdfAcroFieldCollection Fields
/// </summary>
public sealed class PdfAcroFieldCollection : PdfArray
{
PdfAcroFieldCollection(PdfDocument document)
: base(document)
{ }

PdfAcroFieldCollection(PdfArray array)
: base(array)
{ }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,33 @@ public PdfAcroField.PdfAcroFieldCollection Fields
}
PdfAcroField.PdfAcroFieldCollection? _fields;

/// <summary>
/// Gets the flattened field-hierarchy of this AcroForm
/// </summary>
public IEnumerable<PdfAcroField> GetAllFields()
{
var fields = new List<PdfAcroField>();
if (Fields != null)
{
for (var i = 0; i < Fields.Elements.Count; i++)
{
var field = Fields[i];
TraverseFields(field, ref fields);
}
}
return fields;
}

private static void TraverseFields(PdfAcroField parentField, ref List<PdfAcroField> fieldList)
{
fieldList.Add(parentField);
for (var i = 0; i < parentField.Fields.Elements.Count; i++)
{
var field = parentField.Fields[i];
TraverseFields(field, ref fieldList);
}
}

/// <summary>
/// Predefined keys of this dictionary.
/// The description comes from PDF 1.4 Reference.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See the LICENSE file in the solution root for more information.

using PdfSharp.Pdf.IO;
using PdfSharp.Pdf.Signatures;

namespace PdfSharp.Pdf.AcroForms
{
Expand All @@ -15,32 +16,44 @@ public sealed class PdfSignatureField : PdfAcroField
/// </summary>
internal PdfSignatureField(PdfDocument document)
: base(document)
{ }
{
Elements[PdfAcroField.Keys.FT] = new PdfName("/Sig");
}

internal PdfSignatureField(PdfDictionary dict)
: base(dict)
{ }

/// <summary>
/// Writes a key/value pair of this signature field dictionary.
/// Gets or sets the value for this field
/// </summary>
internal override void WriteDictionaryElement(PdfWriter writer, PdfName key)
public new PdfSignatureValue? Value
{
// Don’t encrypt Contents key’s value (PDF Reference 2.0: 7.6.2, Page 71).
if (key.Value == Keys.Contents)
get
{
if (sigValue is null)
{
var dict = Elements.GetValue(PdfAcroField.Keys.V) as PdfDictionary;
if (dict is not null)
sigValue = new PdfSignatureValue(dict);
}
return sigValue;
}
set
{
var effectiveSecurityHandler = writer.EffectiveSecurityHandler;
writer.EffectiveSecurityHandler = null;
base.WriteDictionaryElement(writer, key);
writer.EffectiveSecurityHandler = effectiveSecurityHandler;
if (value is not null)
Elements.SetReference(PdfAcroField.Keys.V, value);
else
Elements.Remove(PdfAcroField.Keys.V);
}
else
base.WriteDictionaryElement(writer, key);
}
PdfSignatureValue? sigValue;

/// <summary>
/// Predefined keys of this dictionary.
/// The description comes from PDF 1.4 Reference.
/// The description comes from PDF 1.4 Reference.<br></br>
/// TODO: These are wrong !
/// The keys are for a <see cref="PdfSignatureValue"/>, not for a <see cref="PdfSignatureField"/>
/// </summary>
public new class Keys : PdfAcroField.Keys
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public PdfPages Pages
if (_pages == null)
{
_pages = (PdfPages?)Elements.GetValue(Keys.Pages, VCF.CreateIndirect) ?? NRT.ThrowOnNull<PdfPages>();
if (Owner.IsImported)
if (Owner.IsImported && Owner._openMode != PdfDocumentOpenMode.Append)
_pages.FlattenPageTree();
}
return _pages;
Expand Down Expand Up @@ -152,14 +152,31 @@ public PdfNameDictionary Names
/// <summary>
/// Gets the AcroForm dictionary of this document.
/// </summary>
public PdfAcroForm AcroForm
public PdfAcroForm? AcroForm
{
get
{
if (_acroForm == null)
_acroForm = (PdfAcroForm?)Elements.GetValue(Keys.AcroForm)??NRT.ThrowOnNull<PdfAcroForm>();
_acroForm = (PdfAcroForm?)Elements.GetValue(Keys.AcroForm);
return _acroForm;
}
internal set
{
if (value != null)
{
if (!value.IsIndirect)
throw new InvalidOperationException("Setting the AcroForm requires an indirect object");
Elements.SetReference(Keys.AcroForm, value);
_acroForm = value;
}
else
{
if (AcroForm != null && AcroForm.Reference != null)
_document.IrefTable.Remove(AcroForm.Reference);
Elements.Remove(Keys.AcroForm);
_acroForm = null;
}
}
}
PdfAcroForm? _acroForm;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public PdfContent(PdfDictionary dict) // HACK PdfContent
: base(dict)
{
// A PdfContent dictionary is always unfiltered.
Decode();
Owner.IrefTable.IgnoreModify(Decode); // decode modifies the object, ignore that
}

/// <summary>
Expand Down Expand Up @@ -135,7 +135,8 @@ internal override void WriteObject(PdfWriter writer)
//Elements["/Filter"] = new PdfName("/FlateDecode");
Elements.SetName("/Filter", "/FlateDecode");
}
Elements.SetInteger("/Length", Stream.Length);
// avoid adding this to "ModifiedObjects" while saving (caused "CollectionWasModified"-Exception)
Owner.IrefTable.IgnoreModify(() => Elements.SetInteger("/Length", Stream.Length));
}

base.WriteObject(writer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public PdfContent AppendContent()
{
Debug.Assert(Owner != null);

SetModified();
if (Owner._openMode != PdfDocumentOpenMode.Append)
SetModified();
PdfContent content = new PdfContent(Owner);
Owner.IrefTable.Add(content);
Debug.Assert(content.Reference != null);
Expand All @@ -64,7 +65,8 @@ public PdfContent PrependContent()
{
Debug.Assert(Owner != null);

SetModified();
if (Owner._openMode != PdfDocumentOpenMode.Append)
SetModified();
PdfContent content = new PdfContent(Owner);
Owner.IrefTable.Add(content);
Debug.Assert(content.Reference != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,52 @@ public PdfCrossReferenceTable(PdfDocument document)
public Dictionary<PdfObjectID, PdfReference> ObjectTable = [];

/// <summary>
/// Used to collect modified objects for incremental updates
/// </summary>
internal Dictionary<PdfObjectID, PdfReference> ModifiedObjects = [];

/// <summary>
/// Used to collect deleted objects for incremental updates
/// </summary>
internal HashSet<PdfObjectID> DeletedObjects = [];

/// Gets or sets a value indicating whether this table is under construction.
/// It is true while reading a PDF file.
/// </summary>
internal bool IsUnderConstruction { get; set; }

internal bool ReadyForModification { get; set; }

internal void MarkAsModified(PdfReference? pdfReference)
{
if (pdfReference == null || !ReadyForModification)
return;

if (pdfReference.ObjectID.IsEmpty)
throw new ArgumentException("ObjectID must not be empty", nameof(pdfReference.ObjectID));

ModifiedObjects[pdfReference.ObjectID] = pdfReference;
}

/// <summary>
/// Used to temporarily ignore modifications to objects<br></br>
/// (i.e. when doing type-transformations that do not change the structure of the document)
/// </summary>
/// <param name="action"></param>
internal void IgnoreModify(Action action)
{
var prev = ReadyForModification;
ReadyForModification = false;
try
{
action();
}
finally
{
ReadyForModification = prev;
}
}

/// <summary>
/// Adds a cross-reference entry to the table. Used when parsing the trailer.
/// </summary>
Expand All @@ -58,6 +99,12 @@ public void Add(PdfReference iref)
#endif
}
ObjectTable.Add(iref.ObjectID, iref);

// new objects must be treated like modified objects for incremental updates
if (ReadyForModification && _document.IsAppending)
{
ModifiedObjects[iref.ObjectID] = iref;
}
}

/// <summary>
Expand All @@ -77,6 +124,12 @@ public void Add(PdfObject value)
throw new InvalidOperationException("Object already in table.");

ObjectTable.Add(value.ObjectID, value.ReferenceNotNull);

// new objects must be treated like modified objects for incremental updates
if (ReadyForModification && _document.IsAppending)
{
ModifiedObjects[value.ObjectID] = value.ReferenceNotNull;
}
}

/// <summary>
Expand Down Expand Up @@ -231,8 +284,7 @@ internal int Compact()
ids.Add(iref.ObjectNumber, 0);
}

//
Dictionary<PdfReference, int> refs = new Dictionary<PdfReference, int>();
var refs = new Dictionary<PdfReference, int>();
foreach (PdfReference iref in irefs)
{
refs.Add(iref, 0);
Expand Down Expand Up @@ -264,7 +316,10 @@ internal int Compact()
#endif

MaxObjectNumber = 0;
// remember list of currently known object-IDs
var allObjectIds = new HashSet<PdfObjectID>(ObjectTable.Keys);
ObjectTable.Clear();
DeletedObjects.Clear();
foreach (PdfReference iref in irefs)
{
// This if is needed for corrupt PDF files from the wild.
Expand All @@ -276,6 +331,12 @@ internal int Compact()
MaxObjectNumber = Math.Max(MaxObjectNumber, iref.ObjectNumber);
}
}
// if initial object-ID is not in the list of final objects, mark as deleted
foreach (var objId in allObjectIds)
{
if (!ObjectTable.ContainsKey(objId))
DeletedObjects.Add(objId);
}
//CheckConsistence();
removed -= ObjectTable.Count;
return removed;
Expand Down
Loading