Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net48'">
<Reference Include="System.IO.Compression" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net40'">
<Reference Include="PresentationCore" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Security" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase">
Expand Down
8 changes: 2 additions & 6 deletions Epplus-FixCalcsEdition/EPPlus/ExcelWorkbook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,7 @@ private void ValidateDataValidations()
}
}

private void SaveSharedStringHandler(ZipOutputStream stream, CompressionLevel compressionLevel, string fileName)
private void SaveSharedStringHandler(Stream stream, CompressionLevel compressionLevel, string fileName)
{
//Packaging.ZipPackagePart stringPart;
//if (_package.Package.PartExists(SharedStringsUri))
Expand All @@ -920,11 +920,7 @@ private void SaveSharedStringHandler(ZipOutputStream stream, CompressionLevel co
//}

//StreamWriter sw = new StreamWriter(stringPart.GetStream(FileMode.Create, FileAccess.Write));
//Init Zip
stream.CompressionLevel = (CompuMaster.Epplus4.Packaging.Ionic.Zlib.CompressionLevel)compressionLevel;
stream.PutNextEntry(fileName);

var cache = new StringBuilder();
var cache = new StringBuilder();
var sw = new StreamWriter(stream);
cache.AppendFormat("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"{0}\" uniqueCount=\"{0}\">", _sharedStrings.Count);
foreach (string t in _sharedStrings.Keys)
Expand Down
8 changes: 1 addition & 7 deletions Epplus-FixCalcsEdition/EPPlus/ExcelWorksheet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@
var xr = XmlReader.Create(stream,new XmlReaderSettings() { DtdProcessing = DtdProcessing.Prohibit, IgnoreWhitespace = true });
#else
var xr = new XmlTextReader(stream);
xr.ProhibitDtd = true;

Check warning on line 888 in Epplus-FixCalcsEdition/EPPlus/ExcelWorksheet.cs

View workflow job for this annotation

GitHub Actions / test (windows-latest)

'XmlTextReader.ProhibitDtd' is obsolete: 'Use DtdProcessing property instead.'

Check warning on line 888 in Epplus-FixCalcsEdition/EPPlus/ExcelWorksheet.cs

View workflow job for this annotation

GitHub Actions / test (macos-latest)

'XmlTextReader.ProhibitDtd' is obsolete: 'Use DtdProcessing property instead.'
xr.WhitespaceHandling = WhitespaceHandling.None;
#endif
LoadColumns(xr); //columnXml
Expand Down Expand Up @@ -2995,14 +2995,8 @@
}
}
}
internal void SaveHandler(ZipOutputStream stream, CompressionLevel compressionLevel, string fileName)
internal void SaveHandler(Stream stream, CompressionLevel compressionLevel, string fileName)
{
//Init Zip
stream.CodecBufferSize = 8096;
stream.CompressionLevel = (CompuMaster.Epplus4.Packaging.Ionic.Zlib.CompressionLevel)compressionLevel;
stream.PutNextEntry(fileName);


SaveXml(stream);
}

Expand Down
141 changes: 112 additions & 29 deletions Epplus-FixCalcsEdition/EPPlus/Packaging/ZipPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Xml;
using CompuMaster.Epplus4.Utils;
using CompuMaster.Epplus4.Packaging.Ionic.Zip;
using Ionic.Zip;
namespace CompuMaster.Epplus4.Packaging
{
/// <summary>
Expand Down Expand Up @@ -96,55 +95,69 @@ internal ZipPackage(Stream stream)
{
var rels = new Dictionary<string, string>();
stream.Seek(0, SeekOrigin.Begin);
using (ZipInputStream zip = new ZipInputStream(stream))
ZipArchive zip = new ZipArchive(stream, ZipArchiveMode.Read, true);
if (zip.Entries.Count == 0)
{
var e = zip.GetNextEntry();
if(e==null)
zip.Dispose();
var repairedStream = RepairEmptyCentralDirectory(stream);
if (repairedStream == null)
{
throw new InvalidDataException("The file is not an valid Package file. If the file is encrypted, please supply the password in the constructor.");
}
zip = new ZipArchive(repairedStream, ZipArchiveMode.Read, false);
}
using (zip)
{
if (zip.Entries.Count == 0)
{
throw (new InvalidDataException("The file is not an valid Package file. If the file is encrypted, please supply the password in the constructor."));
}
if (e.FileName.Contains("\\"))
if (zip.Entries[0].FullName.Contains("\\"))
{
_dirSeparator = '\\';
}
else
{
_dirSeparator = '/';
}
while (e != null)
foreach (ZipArchiveEntry e in zip.Entries)
{
if (e.UncompressedSize > 0)
if (e.Length > 0)
{
var b = new byte[e.UncompressedSize];
var size = zip.Read(b, 0, (int)e.UncompressedSize);
if (e.FileName.Equals("[content_types].xml", StringComparison.OrdinalIgnoreCase))
byte[] b;
using (var entryStream = e.Open())
using (var buffer = new MemoryStream())
{
entryStream.CopyTo(buffer);
b = buffer.ToArray();
}
if (e.FullName.Equals("[content_types].xml", StringComparison.OrdinalIgnoreCase))
{
AddContentTypes(Encoding.UTF8.GetString(b));
hasContentTypeXml = true;
}
else if (e.FileName.Equals($"_rels{_dirSeparator}.rels", StringComparison.OrdinalIgnoreCase))
else if (e.FullName.Equals($"_rels{_dirSeparator}.rels", StringComparison.OrdinalIgnoreCase))
{
ReadRelation(Encoding.UTF8.GetString(b), "");
}
else
{
if (e.FileName.EndsWith(".rels", StringComparison.OrdinalIgnoreCase))
if (e.FullName.EndsWith(".rels", StringComparison.OrdinalIgnoreCase))
{
rels.Add(GetUriKey(e.FileName), Encoding.UTF8.GetString(b));
rels.Add(GetUriKey(e.FullName), Encoding.UTF8.GetString(b));
}
else
{
var part = new ZipPackagePart(this, e);
part.Stream = new MemoryStream();
part.Stream.Write(b, 0, b.Length);
Parts.Add(GetUriKey(e.FileName), part);
Parts.Add(GetUriKey(e.FullName), part);
}
}
}
else
{
}
e = zip.GetNextEntry();
}

foreach (var p in Parts)
Expand Down Expand Up @@ -173,12 +186,69 @@ internal ZipPackage(Stream stream)
{
throw (new InvalidDataException("The file is not an valid Package file. If the file is encrypted, please supply the password in the constructor."));
}
zip.Close();
zip.Dispose();
}
}
}

// Some older encrypted workbooks have valid central directory records but an
// end-of-central-directory record whose entry count and offsets are all zero.
// The former streaming ZIP reader ignored that record. Repair a private copy
// only when the complete central directory chain can be validated.
private static Stream RepairEmptyCentralDirectory(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
byte[] data;
using (var copy = new MemoryStream())
{
stream.CopyTo(copy);
data = copy.ToArray();
}
int end = data.Length - 22;
if (end < 46 || !HasZipSignature(data, 0, 0x04034b50) ||
!HasZipSignature(data, end, 0x06054b50))
{
return null;
}
for (int offset = end + 4; offset < data.Length; offset++)
{
if (data[offset] != 0) return null;
}

int first = end;
int count = 0;
while (first >= 46 && count < ushort.MaxValue)
{
bool found = false;
for (int offset = first - 46; offset >= 0; offset--)
{
if (!HasZipSignature(data, offset, 0x02014b50)) continue;
int recordLength = 46 + BitConverter.ToUInt16(data, offset + 28) +
BitConverter.ToUInt16(data, offset + 30) + BitConverter.ToUInt16(data, offset + 32);
uint localOffset = BitConverter.ToUInt32(data, offset + 42);
if ((long)offset + recordLength != first || localOffset >= offset ||
!HasZipSignature(data, (int)localOffset, 0x04034b50)) continue;
first = offset;
count++;
found = true;
break;
}
if (!found) break;
}
if (count == 0 || first == 0) return null;

Buffer.BlockCopy(BitConverter.GetBytes((ushort)count), 0, data, end + 8, 2);
Buffer.BlockCopy(BitConverter.GetBytes((ushort)count), 0, data, end + 10, 2);
Buffer.BlockCopy(BitConverter.GetBytes((uint)(end - first)), 0, data, end + 12, 4);
Buffer.BlockCopy(BitConverter.GetBytes((uint)first), 0, data, end + 16, 4);
return new MemoryStream(data, false);
}

private static bool HasZipSignature(byte[] data, int offset, uint signature)
{
return offset >= 0 && offset <= data.Length - 4 &&
BitConverter.ToUInt32(data, offset) == signature;
}

private void AddContentTypes(string xml)
{
var doc = new XmlDocument();
Expand Down Expand Up @@ -276,20 +346,23 @@ internal void DeletePart(Uri Uri)
internal void Save(Stream stream)
{
var enc = Encoding.UTF8;
ZipOutputStream os = new ZipOutputStream(stream, true);
os.CompressionLevel = (CompuMaster.Epplus4.Packaging.Ionic.Zlib.CompressionLevel)_compression;
using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
/**** ContentType****/
var entry = os.PutNextEntry("[Content_Types].xml");
var entry = archive.CreateEntry("[Content_Types].xml", GetZipCompressionLevel(_compression));
byte[] b = enc.GetBytes(GetContentTypeXml());
os.Write(b, 0, b.Length);
using (var entryStream = entry.Open())
{
entryStream.Write(b, 0, b.Length);
}
/**** Top Rels ****/
_rels.WriteZip(os, $"_rels/.rels");
_rels.WriteZip(archive, $"_rels/.rels", _compression);
ZipPackagePart ssPart=null;
foreach(var part in Parts.Values)
{
if (part.ContentType != ExcelPackage.contentTypeSharedString)
{
part.WriteZip(os);
part.WriteZip(archive);
}
else
{
Expand All @@ -299,16 +372,26 @@ internal void Save(Stream stream)
//Shared strings must be saved after all worksheets. The ss dictionary is populated when that workheets are saved (to get the best performance).
if (ssPart != null)
{
ssPart.WriteZip(os);
ssPart.WriteZip(archive);
}
}
os.Flush();

os.Close();
os.Dispose();

//return ms;
}

internal static System.IO.Compression.CompressionLevel GetZipCompressionLevel(CompressionLevel level)
{
if (level == CompressionLevel.None)
{
return System.IO.Compression.CompressionLevel.NoCompression;
}
if ((int)level <= 3)
{
return System.IO.Compression.CompressionLevel.Fastest;
}
return System.IO.Compression.CompressionLevel.Optimal;
}

private string GetContentTypeXml()
{
StringBuilder xml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">");
Expand Down
27 changes: 17 additions & 10 deletions Epplus-FixCalcsEdition/EPPlus/Packaging/ZipPackagePart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,21 @@
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using CompuMaster.Epplus4.Packaging.Ionic.Zip;

namespace CompuMaster.Epplus4.Packaging
{
internal class ZipPackagePart : ZipPackageRelationshipBase, IDisposable
{
internal delegate void SaveHandlerDelegate(ZipOutputStream stream, CompressionLevel compressionLevel, string fileName);
internal delegate void SaveHandlerDelegate(Stream stream, CompressionLevel compressionLevel, string fileName);

internal ZipPackagePart(ZipPackage package, ZipEntry entry)
internal ZipPackagePart(ZipPackage package, ZipArchiveEntry entry)
{
Package = package;
Entry = entry;
SaveHandler = null;
Uri = new Uri(package.GetUriKey(entry.FileName), UriKind.Relative);
Uri = new Uri(package.GetUriKey(entry.FullName), UriKind.Relative);
}
internal ZipPackagePart(ZipPackage package, Uri partUri, string contentType, CompressionLevel compressionLevel)
{
Expand All @@ -58,7 +59,7 @@ internal ZipPackagePart(ZipPackage package, Uri partUri, string contentType, Com
CompressionLevel = compressionLevel;
}
internal ZipPackage Package { get; set; }
internal ZipEntry Entry { get; set; }
internal ZipArchiveEntry Entry { get; set; }
internal CompressionLevel CompressionLevel;
MemoryStream _stream = null;
internal MemoryStream Stream
Expand Down Expand Up @@ -132,7 +133,7 @@ internal SaveHandlerDelegate SaveHandler
get;
set;
}
internal void WriteZip(ZipOutputStream os)
internal void WriteZip(ZipArchive archive)
{
byte[] b;
if (SaveHandler == null)
Expand All @@ -142,20 +143,26 @@ internal void WriteZip(ZipOutputStream os)
{
return;
}
os.CompressionLevel = (CompuMaster.Epplus4.Packaging.Ionic.Zlib.CompressionLevel)CompressionLevel;
os.PutNextEntry(Uri.OriginalString);
os.Write(b, 0, b.Length);
var entry = archive.CreateEntry(Uri.OriginalString, ZipPackage.GetZipCompressionLevel(CompressionLevel));
using (var entryStream = entry.Open())
{
entryStream.Write(b, 0, b.Length);
}
}
else
{
SaveHandler(os, (CompressionLevel)CompressionLevel, Uri.OriginalString);
var entry = archive.CreateEntry(Uri.OriginalString, ZipPackage.GetZipCompressionLevel(CompressionLevel));
using (var entryStream = entry.Open())
{
SaveHandler(entryStream, CompressionLevel, Uri.OriginalString);
}
}

if (_rels.Count > 0)
{
string f = Uri.OriginalString;
var name = Path.GetFileName(f);
_rels.WriteZip(os, (string.Format("{0}_rels/{1}.rels", f.Substring(0, f.Length - name.Length), name)));
_rels.WriteZip(archive, (string.Format("{0}_rels/{1}.rels", f.Substring(0, f.Length - name.Length), name)), CompressionLevel);
}
b = null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
using System.Text;
using Ionic.Zip;
using System.IO;
using System.IO.Compression;
using System.Security;
using CompuMaster.Epplus4.Packaging.Ionic.Zip;

Expand Down Expand Up @@ -84,7 +85,7 @@ internal ZipPackageRelationshipCollection GetRelationshipsByType(string relation
return ret;
}

internal void WriteZip(ZipOutputStream os, string fileName)
internal void WriteZip(ZipArchive archive, string fileName, CompressionLevel compressionLevel)
{
StringBuilder xml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">");
foreach (var rel in _rels.Values)
Expand All @@ -93,9 +94,12 @@ internal void WriteZip(ZipOutputStream os, string fileName)
}
xml.Append("</Relationships>");

os.PutNextEntry(fileName);
var entry = archive.CreateEntry(fileName, ZipPackage.GetZipCompressionLevel(compressionLevel));
byte[] b = Encoding.UTF8.GetBytes(xml.ToString());
os.Write(b, 0, b.Length);
using (var entryStream = entry.Open())
{
entryStream.Write(b, 0, b.Length);
}
}

public int Count
Expand Down
Loading
Loading