write valid zlib stream for flate

since c# only produces a deflate stream when compressing it is necessary to provide the header and footer bytes to convert this to a valid zlib stream. this involves setting the correct 2 bytes for the header and appending a 4 byte adler checksum for the uncompressed data after the compressed data stream.
This commit is contained in:
Eliot Jones
2020-01-04 10:15:06 +00:00
parent b7554e2838
commit b355a31ae8
2 changed files with 74 additions and 8 deletions

View File

@@ -27,6 +27,9 @@
private const int DefaultBitsPerComponent = 8; private const int DefaultBitsPerComponent = 8;
private const int DefaultColumns = 1; private const int DefaultColumns = 1;
private const byte Deflate32KbWindow = 120;
private const byte ChecksumBits = 1;
private readonly IDecodeParameterResolver decodeParameterResolver; private readonly IDecodeParameterResolver decodeParameterResolver;
private readonly IPngPredictor pngPredictor; private readonly IPngPredictor pngPredictor;
private readonly ILog log; private readonly ILog log;
@@ -106,17 +109,45 @@
public byte[] Encode(Stream input, DictionaryToken streamDictionary, int index) public byte[] Encode(Stream input, DictionaryToken streamDictionary, int index)
{ {
var resx = new List<byte>{ 120, 156 }; const int headerLength = 2;
using (var output = new MemoryStream()) const int checksumLength = 4;
using (var flater = new DeflateStream(output, CompressionMode.Compress, true))
{
input.CopyTo(flater);
flater.Close();
resx.AddRange(output.ToArray()); byte[] data;
using (var temp = new MemoryStream())
{
input.CopyTo(temp);
data = temp.ToArray();
} }
return resx.ToArray(); using (var compressStream = new MemoryStream())
using (var compressor = new DeflateStream(compressStream, CompressionLevel.Fastest))
{
compressor.Write(data, 0, data.Length);
compressor.Close();
var compressed = compressStream.ToArray();
var result = new byte[headerLength + compressed.Length + checksumLength];
// Write the ZLib header.
result[0] = Deflate32KbWindow;
result[1] = ChecksumBits;
// Write the compressed data.
Array.Copy(compressed, 0, result, headerLength, compressed.Length);
// Write Checksum of raw data.
var checksum = Adler32Checksum.Calculate(data);
var offset = headerLength + compressed.Length;
result[offset++] = (byte)(checksum >> 24);
result[offset++] = (byte)(checksum >> 16);
result[offset++] = (byte)(checksum >> 8);
result[offset] = (byte)(checksum >> 0);
return result;
}
} }
} }
} }

View File

@@ -0,0 +1,35 @@
namespace UglyToad.PdfPig.Util
{
using System.Collections.Generic;
/// <summary>
/// Used to calculate the Adler-32 checksum used for ZLIB data in accordance with
/// RFC 1950: ZLIB Compressed Data Format Specification.
/// </summary>
public static class Adler32Checksum
{
// Both sums (s1 and s2) are done modulo 65521.
private const int AdlerModulus = 65521;
/// <summary>
/// Calculate the Adler-32 checksum for some data.
/// </summary>
public static int Calculate(IEnumerable<byte> data)
{
// s1 is the sum of all bytes.
var s1 = 1;
// s2 is the sum of all s1 values.
var s2 = 0;
foreach (var b in data)
{
s1 = (s1 + b) % AdlerModulus;
s2 = (s1 + s2) % AdlerModulus;
}
// The Adler-32 checksum is stored as s2*65536 + s1.
return s2 * 65536 + s1;
}
}
}