compress page content stream

when writing a new pdf document we now use the flate filter to compress the page content stream. we also move letters in the same word into the same showtext operation.
This commit is contained in:
Eliot Jones
2020-01-04 10:04:02 +00:00
parent 694388f9b6
commit b7554e2838
4 changed files with 65 additions and 45 deletions

View File

@@ -0,0 +1,38 @@
namespace UglyToad.PdfPig.Writer
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Filters;
using Logging;
using Tokens;
internal static class DataCompresser
{
public static byte[] CompressBytes(IReadOnlyList<byte> bytes) => CompressBytes(bytes.ToArray());
public static byte[] CompressBytes(byte[] bytes)
{
using (var memoryStream = new MemoryStream(bytes))
{
var parameters = new DictionaryToken(new Dictionary<NameToken, IToken>());
var flater = new FlateFilter(new DecodeParameterResolver(new NoOpLog()), new PngPredictor(), new NoOpLog());
var result = flater.Encode(memoryStream, parameters, 0);
return result;
}
}
public static StreamToken CompressToStream(IReadOnlyList<byte> bytes) => CompressToStream(bytes.ToArray());
public static StreamToken CompressToStream(byte[] bytes)
{
var compressed = CompressBytes(bytes);
var stream = new StreamToken(new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Length, new NumericToken(compressed.Length) },
{ NameToken.Length1, new NumericToken(bytes.Length) },
{ NameToken.Filter, new ArrayToken(new []{ NameToken.FlateDecode }) }
}), compressed);
return stream;
}
}
}

View File

@@ -5,9 +5,7 @@
using System.IO;
using System.Linq;
using Core;
using Filters;
using Geometry;
using Logging;
using Tokens;
using PdfPig.Fonts;
using PdfPig.Fonts.Exceptions;
@@ -52,17 +50,10 @@
public ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context)
{
var b = TrueTypeSubsetter.Subset(fontFileBytes.ToArray(), new TrueTypeSubsetEncoding(characterMapping.Keys.ToList()));
var newEncoding = new TrueTypeSubsetEncoding(characterMapping.Keys.ToList());
var subsetBytes = TrueTypeSubsetter.Subset(fontFileBytes.ToArray(), newEncoding);
// A symbolic font (one which contains characters not in the standard latin set) -
// should contain a MacRoman (1, 0) or Windows Symbolic (3,0) cmap subtable which maps character codes to glyph id.
var bytes = CompressBytes(b);
var embeddedFile = new StreamToken(new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Length, new NumericToken(bytes.Length) },
{ NameToken.Length1, new NumericToken(b.Length) },
{ NameToken.Filter, new ArrayToken(new []{ NameToken.FlateDecode }) }
}), bytes);
var embeddedFile = DataCompresser.CompressToStream(subsetBytes);
var fileRef = context.WriteObject(outputStream, embeddedFile);
@@ -82,8 +73,8 @@
{ NameToken.Flags, new NumericToken((int)FontDescriptorFlags.Symbolic) },
{ NameToken.FontBbox, GetBoundingBox(bbox, scaling) },
{ NameToken.ItalicAngle, new NumericToken(postscript.ItalicAngle) },
{ NameToken.Ascent, new NumericToken(hhead.Ascent * scaling) },
{ NameToken.Descent, new NumericToken(hhead.Descent * scaling) },
{ NameToken.Ascent, new NumericToken(Math.Round(hhead.Ascent * scaling, 2)) },
{ NameToken.Descent, new NumericToken(Math.Round(hhead.Descent * scaling, 2)) },
{ NameToken.CapHeight, new NumericToken(90) },
{ NameToken.StemV, new NumericToken(90) },
{ NameToken.FontFile2, new IndirectReferenceToken(fileRef.Number) }
@@ -121,13 +112,8 @@
var descriptor = context.WriteObject(outputStream, new DictionaryToken(descriptorDictionary));
var toUnicodeCMap = ToUnicodeCMapBuilder.ConvertToCMapStream(characterMapping);
var compressedToUnicodeCMap = CompressBytes(toUnicodeCMap);
var toUnicode = context.WriteObject(outputStream, new StreamToken(new DictionaryToken(new Dictionary<NameToken, IToken>()
{
{NameToken.Length, new NumericToken(compressedToUnicodeCMap.Length)},
{NameToken.Length1, new NumericToken(toUnicodeCMap.Count)},
{NameToken.Filter, new ArrayToken(new[] {NameToken.FlateDecode})}
}), compressedToUnicodeCMap));
var toUnicodeStream = DataCompresser.CompressToStream(toUnicodeCMap);
var toUnicode = context.WriteObject(outputStream, toUnicodeStream);
var dictionary = new Dictionary<NameToken, IToken>
{
@@ -174,25 +160,14 @@
}
}
private static byte[] CompressBytes(IReadOnlyList<byte> bytes)
{
using (var memoryStream = new MemoryStream(bytes.ToArray()))
{
var parameters = new DictionaryToken(new Dictionary<NameToken, IToken>());
var flater = new FlateFilter(new DecodeParameterResolver(new NoOpLog()), new PngPredictor(), new NoOpLog());
var result = flater.Encode(memoryStream, parameters, 0);
return result;
}
}
private static ArrayToken GetBoundingBox(PdfRectangle boundingBox, decimal scaling)
{
return new ArrayToken(new[]
{
new NumericToken((decimal)boundingBox.Left * scaling),
new NumericToken((decimal)boundingBox.Bottom * scaling),
new NumericToken((decimal)boundingBox.Right * scaling),
new NumericToken((decimal)boundingBox.Top * scaling)
new NumericToken(Math.Round((decimal)boundingBox.Left * scaling, 2)),
new NumericToken(Math.Round((decimal)boundingBox.Bottom * scaling, 2)),
new NumericToken(Math.Round((decimal)boundingBox.Right * scaling, 2)),
new NumericToken(Math.Round((decimal)boundingBox.Top * scaling, 2))
});
}
}

View File

@@ -295,13 +295,8 @@
var bytes = memoryStream.ToArray();
var streamDictionary = new Dictionary<NameToken, IToken>
{
{ NameToken.Length, new NumericToken(bytes.Length) }
};
var stream = new StreamToken(new DictionaryToken(streamDictionary), bytes);
var stream = DataCompresser.CompressToStream(bytes);
return stream;
}
}

View File

@@ -228,10 +228,22 @@
operations.Add(BeginText.Value);
operations.Add(new SetFontAndSize(font.Name, fontSize));
operations.Add(new MoveToNextLineWithOffset((decimal)position.X, (decimal)position.Y));
var bytesPerShow = new List<byte>();
foreach (var letter in text)
{
if (char.IsWhiteSpace(letter))
{
operations.Add(new ShowText(bytesPerShow.ToArray()));
bytesPerShow.Clear();
}
var b = fontProgram.GetValueForCharacter(letter);
operations.Add(new ShowText(new [] { b }));
bytesPerShow.Add(b);
}
if (bytesPerShow.Count > 0)
{
operations.Add(new ShowText(bytesPerShow.ToArray()));
}
operations.Add(EndText.Value);
@@ -294,7 +306,7 @@
private static decimal RgbToDecimal(byte value)
{
var res = Math.Max(0, value / (decimal)byte.MaxValue);
res = Math.Min(1, res);
res = Math.Round(Math.Min(1, res), 4);
return res;
}