diff --git a/src/UglyToad.PdfPig.Tests/Writer/TokenWriterTests.cs b/src/UglyToad.PdfPig.Tests/Writer/TokenWriterTests.cs
index 017a9b80..18bc7d95 100644
--- a/src/UglyToad.PdfPig.Tests/Writer/TokenWriterTests.cs
+++ b/src/UglyToad.PdfPig.Tests/Writer/TokenWriterTests.cs
@@ -12,10 +12,11 @@
[Fact]
public void EscapeSpecialCharacter()
{
+ var writer = new TokenWriter();
using (var memStream = new MemoryStream())
{
- TokenWriter.WriteToken(new StringToken("\\"), memStream);
- TokenWriter.WriteToken(new StringToken("(Hello)"), memStream);
+ writer.WriteToken(new StringToken("\\"), memStream);
+ writer.WriteToken(new StringToken("(Hello)"), memStream);
// Read Test
memStream.Position = 0;
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContent.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContent.cs
index 17121168..b81c9759 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContent.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContent.cs
@@ -10,6 +10,8 @@
///
public class BeginMarkedContent : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContentWithProperties.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContentWithProperties.cs
index 73e7cdbe..75063865 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContentWithProperties.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/BeginMarkedContentWithProperties.cs
@@ -11,6 +11,8 @@
///
public class BeginMarkedContentWithProperties : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPoint.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPoint.cs
index a78d2b91..c8aeb3c5 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPoint.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPoint.cs
@@ -10,6 +10,8 @@
///
public class DesignateMarkedContentPoint : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPointWithProperties.cs b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPointWithProperties.cs
index 53fe8aeb..0f282fc0 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPointWithProperties.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/MarkedContent/DesignateMarkedContentPointWithProperties.cs
@@ -11,6 +11,8 @@
///
public class DesignateMarkedContentPointWithProperties : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorAdvanced.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorAdvanced.cs
index e1be2adb..42652862 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorAdvanced.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorAdvanced.cs
@@ -13,6 +13,8 @@
///
public class SetNonStrokeColorAdvanced : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorSpace.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorSpace.cs
index 7715e82d..5c06d741 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorSpace.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/SetNonStrokeColorSpace.cs
@@ -11,6 +11,8 @@
///
public class SetNonStrokeColorSpace : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorAdvanced.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorAdvanced.cs
index 852f4f89..825ece3d 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorAdvanced.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorAdvanced.cs
@@ -13,6 +13,8 @@
///
public class SetStrokeColorAdvanced : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorSpace.cs b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorSpace.cs
index 8c03e731..4ae622da 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorSpace.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/SetStrokeColorSpace.cs
@@ -11,6 +11,8 @@
///
public class SetStrokeColorSpace : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/TextShowing/ShowTextsWithPositioning.cs b/src/UglyToad.PdfPig/Graphics/Operations/TextShowing/ShowTextsWithPositioning.cs
index 78e2d4b1..08695238 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/TextShowing/ShowTextsWithPositioning.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/TextShowing/ShowTextsWithPositioning.cs
@@ -15,6 +15,8 @@
///
public class ShowTextsWithPositioning : IGraphicsStateOperation
{
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
///
/// The symbol for this operation in a stream.
///
diff --git a/src/UglyToad.PdfPig/Writer/Fonts/ToUnicodeCMapBuilder.cs b/src/UglyToad.PdfPig/Writer/Fonts/ToUnicodeCMapBuilder.cs
index 6015bf0d..41768266 100644
--- a/src/UglyToad.PdfPig/Writer/Fonts/ToUnicodeCMapBuilder.cs
+++ b/src/UglyToad.PdfPig/Writer/Fonts/ToUnicodeCMapBuilder.cs
@@ -14,6 +14,8 @@
private const string DictToken = "dict";
private const string FindResourceToken = "findresource";
+ private static readonly TokenWriter TokenWriter = new TokenWriter();
+
public static IReadOnlyList ConvertToCMapStream(IReadOnlyDictionary unicodeToCharacterCode)
{
using (var memoryStream = new MemoryStream())
diff --git a/src/UglyToad.PdfPig/Writer/ITokenWriter.cs b/src/UglyToad.PdfPig/Writer/ITokenWriter.cs
new file mode 100644
index 00000000..ea851127
--- /dev/null
+++ b/src/UglyToad.PdfPig/Writer/ITokenWriter.cs
@@ -0,0 +1,17 @@
+namespace UglyToad.PdfPig.Writer;
+
+using System.IO;
+using Tokens;
+
+///
+/// Writes any type of to the corresponding PDF document format output.
+///
+public interface ITokenWriter
+{
+ ///
+ /// Writes the given input token to the output stream with the correct PDF format and encoding including whitespace and line breaks as applicable.
+ ///
+ /// The token to write to the stream.
+ /// The stream to write the token to.
+ void WriteToken(IToken token, Stream outputStream);
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Writer/PdfStreamWriter.cs b/src/UglyToad.PdfPig/Writer/PdfStreamWriter.cs
index 65d7ec23..364a57ef 100644
--- a/src/UglyToad.PdfPig/Writer/PdfStreamWriter.cs
+++ b/src/UglyToad.PdfPig/Writer/PdfStreamWriter.cs
@@ -18,6 +18,7 @@
protected bool DisposeStream { get; set; }
protected bool Initialized { get; set; }
protected int CurrentNumber { get; set; } = 1;
+ protected readonly static TokenWriter TokenWriter = new TokenWriter();
internal PdfStreamWriter(Stream baseStream, bool disposeStream = true)
{
diff --git a/src/UglyToad.PdfPig/Writer/TokenWriter.cs b/src/UglyToad.PdfPig/Writer/TokenWriter.cs
index 88202ad6..50cbb2ef 100644
--- a/src/UglyToad.PdfPig/Writer/TokenWriter.cs
+++ b/src/UglyToad.PdfPig/Writer/TokenWriter.cs
@@ -14,10 +14,8 @@
///
/// Writes any type of to the corresponding PDF document format output.
///
- public class TokenWriter
+ public class TokenWriter : ITokenWriter
{
- private static readonly byte Backslash = GetByte("\\");
-
private static readonly byte ArrayStart = GetByte("[");
private static readonly byte ArrayEnd = GetByte("]");
@@ -46,10 +44,18 @@
private static readonly byte[] StartXref = OtherEncodings.StringAsLatin1Bytes("startxref");
- private static readonly byte[] StreamStart = OtherEncodings.StringAsLatin1Bytes("stream");
- private static readonly byte[] StreamEnd = OtherEncodings.StringAsLatin1Bytes("endstream");
+ ///
+ /// Bytes that indicate start of stream
+ ///
+ protected static readonly byte[] StreamStart = OtherEncodings.StringAsLatin1Bytes("stream");
+
+ ///
+ /// Bytes that indicate end start of stream
+ ///
+ protected static readonly byte[] StreamEnd = OtherEncodings.StringAsLatin1Bytes("endstream");
private static readonly byte StringStart = GetByte("(");
+
private static readonly byte StringEnd = GetByte(")");
private static readonly byte[] Trailer = OtherEncodings.StringAsLatin1Bytes("trailer");
@@ -79,7 +85,7 @@
///
/// The token to write to the stream.
/// The stream to write the token to.
- public static void WriteToken(IToken token, Stream outputStream)
+ public void WriteToken(IToken token, Stream outputStream)
{
if (token == null)
{
@@ -136,7 +142,7 @@
/// The object representing the catalog dictionary which is referenced from the trailer dictionary.
/// The output stream to write to.
/// The object reference for the document information dictionary if present.
- internal static void WriteCrossReferenceTable(IReadOnlyDictionary objectOffsets,
+ internal void WriteCrossReferenceTable(IReadOnlyDictionary objectOffsets,
IndirectReference catalogToken,
Stream outputStream,
IndirectReference? documentInformationReference)
@@ -278,7 +284,7 @@
/// Generation of the indirect object.
/// Pre-serialized object contents.
/// The stream to write the token to.
- internal static void WriteObject(long objectNumber, int generation, byte[] data, Stream outputStream)
+ internal void WriteObject(long objectNumber, int generation, byte[] data, Stream outputStream)
{
WriteLong(objectNumber, outputStream);
WriteWhitespace(outputStream);
@@ -297,14 +303,24 @@
WriteLineBreak(outputStream);
}
- private static void WriteHex(HexToken hex, Stream stream)
+ ///
+ /// Write a hex value to the output stream
+ ///
+ ///
+ ///
+ protected void WriteHex(HexToken hex, Stream stream)
{
stream.WriteByte(HexStart);
stream.WriteText(hex.GetHexString());
stream.WriteByte(HexEnd);
}
- private static void WriteArray(ArrayToken array, Stream outputStream)
+ ///
+ /// Write an array to the output stream, with whitespace at the end.
+ ///
+ ///
+ ///
+ protected void WriteArray(ArrayToken array, Stream outputStream)
{
outputStream.WriteByte(ArrayStart);
WriteWhitespace(outputStream);
@@ -319,14 +335,24 @@
WriteWhitespace(outputStream);
}
- private static void WriteBoolean(BooleanToken boolean, Stream outputStream)
+ ///
+ /// Write a boolean "true" or "false" to the output stream, with whitespace at the end.
+ ///
+ ///
+ ///
+ protected void WriteBoolean(BooleanToken boolean, Stream outputStream)
{
var bytes = boolean.Data ? TrueBytes : FalseBytes;
outputStream.Write(bytes, 0, bytes.Length);
WriteWhitespace(outputStream);
}
- private static void WriteComment(CommentToken comment, Stream outputStream)
+ ///
+ /// Write a "%comment" in the output stream, with a line break at the end.
+ ///
+ ///
+ ///
+ protected void WriteComment(CommentToken comment, Stream outputStream)
{
var bytes = OtherEncodings.StringAsLatin1Bytes(comment.Data);
outputStream.WriteByte(Comment);
@@ -334,7 +360,12 @@
WriteLineBreak(outputStream);
}
- private static void WriteDictionary(DictionaryToken dictionary, Stream outputStream)
+ ///
+ /// Writes dictionary key/value pairs to output stream as Name/Token pairs.
+ ///
+ ///
+ ///
+ protected void WriteDictionary(DictionaryToken dictionary, Stream outputStream)
{
outputStream.Write(DictionaryStart, 0, DictionaryStart.Length);
@@ -356,7 +387,12 @@
outputStream.Write(DictionaryEnd, 0, DictionaryEnd.Length);
}
- private static void WriteIndirectReference(IndirectReferenceToken reference, Stream outputStream)
+ ///
+ /// Write an indirect reference to the stream, with whitespace at the end.
+ ///
+ ///
+ ///
+ protected virtual void WriteIndirectReference(IndirectReferenceToken reference, Stream outputStream)
{
WriteLong(reference.Data.ObjectNumber, outputStream);
WriteWhitespace(outputStream);
@@ -368,12 +404,17 @@
WriteWhitespace(outputStream);
}
- private static void WriteName(NameToken name, Stream outputStream)
+ ///
+ /// Write a name to the stream, with whitespace at the end.
+ ///
+ ///
+ ///
+ protected virtual void WriteName(NameToken name, Stream outputStream)
{
WriteName(name.Data, outputStream);
}
- private static void WriteName(string name, Stream outputStream)
+ private void WriteName(string name, Stream outputStream)
{
/*
* Beginning with PDF 1.2, any character except null (character code 0) may be
@@ -404,7 +445,12 @@
WriteWhitespace(outputStream);
}
- private static void WriteNumber(NumericToken number, Stream outputStream)
+ ///
+ /// Write a number to the stream, with whitespace at the end.
+ ///
+ ///
+ ///
+ protected virtual void WriteNumber(NumericToken number, Stream outputStream)
{
if (!number.HasDecimalPlaces)
{
@@ -419,7 +465,15 @@
WriteWhitespace(outputStream);
}
- private static void WriteObject(ObjectToken objectToken, Stream outputStream)
+ ///
+ /// Write an object to the stream, with a line break at the end. It writes the following contents:
+ /// - "[ObjectNumber] [Generation] obj"
+ /// - Object data
+ /// - "endobj"
+ ///
+ ///
+ ///
+ protected virtual void WriteObject(ObjectToken objectToken, Stream outputStream)
{
WriteLong(objectToken.Number.ObjectNumber, outputStream);
WriteWhitespace(outputStream);
@@ -438,7 +492,16 @@
WriteLineBreak(outputStream);
}
- private static void WriteStream(StreamToken streamToken, Stream outputStream)
+ ///
+ /// Write a stream token to the output stream, with the following contents:
+ /// - Dictionary specifying the length of the stream, any applied compression filters and additional information.
+ /// - Stream start indicator
+ /// - Bytes in the StreamToken data
+ /// - Stream end indicator
+ ///
+ ///
+ ///
+ protected virtual void WriteStream(StreamToken streamToken, Stream outputStream)
{
WriteDictionary(streamToken.StreamDictionary, outputStream);
WriteLineBreak(outputStream);
@@ -449,15 +512,22 @@
outputStream.Write(StreamEnd, 0, StreamEnd.Length);
}
- private static int[] EscapeNeeded = new int[]
+ private static readonly int[] EscapeNeeded = new int[]
{
'\r', '\n', '\t', '\b', '\f', '\\'
};
- private static int[] Escaped = new int[]
+
+ private static readonly int[] Escaped = new int[]
{
'r', 'n', 't', 'b', 'f', '\\'
};
- private static void WriteString(StringToken stringToken, Stream outputStream)
+
+ ///
+ /// Write string to the stream, with whitespace at the end
+ ///
+ ///
+ ///
+ protected virtual void WriteString(StringToken stringToken, Stream outputStream)
{
outputStream.WriteByte(StringStart);
@@ -515,29 +585,47 @@
WriteWhitespace(outputStream);
}
- private static void WriteInt(int value, Stream outputStream)
+ ///
+ /// Write an integer to the stream
+ ///
+ ///
+ ///
+ protected virtual void WriteInt(int value, Stream outputStream)
{
var bytes = OtherEncodings.StringAsLatin1Bytes(value.ToString("G", CultureInfo.InvariantCulture));
outputStream.Write(bytes, 0, bytes.Length);
}
- private static void WriteLineBreak(Stream outputStream)
+ ///
+ /// Write a line break to the output stream
+ ///
+ ///
+ protected virtual void WriteLineBreak(Stream outputStream)
{
outputStream.WriteNewLine();
}
- private static void WriteLong(long value, Stream outputStream)
+ ///
+ /// Write a long to the stream
+ ///
+ ///
+ ///
+ protected virtual void WriteLong(long value, Stream outputStream)
{
var bytes = OtherEncodings.StringAsLatin1Bytes(value.ToString("G", CultureInfo.InvariantCulture));
outputStream.Write(bytes, 0, bytes.Length);
}
- private static void WriteWhitespace(Stream outputStream)
+ ///
+ /// Write a space to the output stream
+ ///
+ ///
+ protected virtual void WriteWhitespace(Stream outputStream)
{
outputStream.WriteByte(Whitespace);
}
- private static void WriteFirstXrefEmptyEntry(Stream outputStream)
+ private void WriteFirstXrefEmptyEntry(Stream outputStream)
{
/*
* The first entry in the table (object number 0) is always free and has a generation number of 65,535;
@@ -591,5 +679,4 @@
}
}
}
-}
-
+}
\ No newline at end of file