diff --git a/src/UglyToad.PdfPig/Writer/PdfStreamWriter.cs b/src/UglyToad.PdfPig/Writer/PdfStreamWriter.cs index 369d099f..ac0f9d47 100644 --- a/src/UglyToad.PdfPig/Writer/PdfStreamWriter.cs +++ b/src/UglyToad.PdfPig/Writer/PdfStreamWriter.cs @@ -1,38 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using UglyToad.PdfPig.Core; -using UglyToad.PdfPig.Graphics.Operations; -using UglyToad.PdfPig.Tokens; - -namespace UglyToad.PdfPig.Writer +namespace UglyToad.PdfPig.Writer { + using System; + using System.Collections.Generic; + using System.IO; + using Core; + using Graphics.Operations; + using Tokens; + /// /// This class would lazily flush all token. Allowing us to make changes to references without need to rewrite the whole stream /// internal class PdfStreamWriter : IDisposable { - private readonly SortedSet reservedNumbers = new SortedSet(); + private readonly List reservedNumbers = new List(); private readonly Dictionary tokenReferences = new Dictionary(); public int CurrentNumber { get; private set; } = 1; - public Stream Stream { get; } + public Stream Stream { get; private set; } public PdfStreamWriter() : this(new MemoryStream()) { } public PdfStreamWriter(Stream baseStream) { - Stream = baseStream; + Stream = baseStream ?? throw new ArgumentNullException(nameof(baseStream)); } public void Flush(decimal version, IndirectReferenceToken catalogReference) { if (catalogReference == null) throw new ArgumentNullException(nameof(catalogReference)); - + WriteString($"%PDF-{version:0.0}", Stream); Stream.WriteText("%"); @@ -57,7 +56,7 @@ namespace UglyToad.PdfPig.Writer if (catalogToken == null && referenceToken == catalogReference) { - catalogToken = new ObjectToken(offset, referenceToken.Data, token); + catalogToken = obj; } } @@ -70,9 +69,9 @@ namespace UglyToad.PdfPig.Writer TokenWriter.WriteCrossReferenceTable(offsets, catalogToken, Stream, null); } - public IndirectReferenceToken WriteObject(IToken token, int? reservedNumber = null) + public IndirectReferenceToken WriteToken(IToken token, int? reservedNumber = null) { - // if you can't consider deduplicating a token. + // if you can't consider deduplicating the token. // It must be because it's referenced by his child element, so you must have reserved a number before hand // Example /Pages Obj var canBeDuplicated = !reservedNumber.HasValue; @@ -84,20 +83,57 @@ namespace UglyToad.PdfPig.Writer } // When we end up writing this token, all of his child would already have been added and checked for duplicate - return AddObject(token, reservedNumber.Value); + return AddToken(token, reservedNumber.Value); } var reference = FindToken(token); if (reference == null) { - // TODO: Check his children - return AddObject(token, CurrentNumber++); + return AddToken(token, CurrentNumber++); } return reference; } + + public int ReserveNumber() + { + var reserved = CurrentNumber; + reservedNumbers.Add(reserved); + CurrentNumber++; + return reserved; + } - private IndirectReferenceToken AddObject(IToken token, int reservedNumber) + public IndirectReferenceToken ReserveNumberToken() + { + return new IndirectReferenceToken(new IndirectReference(ReserveNumber(), 0)); + } + + public byte[] ToArray() + { + if (!Stream.CanSeek) + throw new NotSupportedException($"{Stream.GetType()} can't seek"); + + var currentPosition = Stream.Position; + Stream.Seek(0, SeekOrigin.Begin); + + var bytes = new byte[Stream.Length]; + + // Should we slice the reading into smaller chunks? + if (Stream.Read(bytes, 0, bytes.Length) != bytes.Length) + throw new Exception("Unable to read all the bytes from stream"); + + Stream.Seek(currentPosition, SeekOrigin.Begin); + + return bytes; + } + + public void Dispose() + { + Stream?.Dispose(); + Stream = null; + } + + private IndirectReferenceToken AddToken(IToken token, int reservedNumber) { var reference = new IndirectReference(reservedNumber, 0); var referenceToken = new IndirectReferenceToken(reference); @@ -119,46 +155,12 @@ namespace UglyToad.PdfPig.Writer return null; } - - public int ReserveNumber() - { - var reserved = CurrentNumber; - reservedNumbers.Add(reserved); - CurrentNumber++; - return reserved; - } - - public IndirectReferenceToken ReserveNumberToken() => new IndirectReferenceToken(new IndirectReference(ReserveNumber(), 0)); - + private static void WriteString(string text, Stream stream) { var bytes = OtherEncodings.StringAsLatin1Bytes(text); stream.Write(bytes, 0, bytes.Length); stream.WriteNewLine(); } - - public byte[] ToArray() - { - if (!Stream.CanSeek) - throw new NotSupportedException("Stream can't seek"); - - var currentPosition = Stream.Position; - Stream.Seek(0, SeekOrigin.Begin); - - var bytes = new byte[Stream.Length]; - - // Should we slice the reading into smaller chunks? - if (Stream.Read(bytes, 0, bytes.Length) != bytes.Length) - throw new Exception("Unable to read all the bytes from stream"); - - Stream.Seek(currentPosition, SeekOrigin.Begin); - - return bytes; - } - - public void Dispose() - { - Stream.Dispose(); - } } }