mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-23 12:43:38 +08:00
#21 create first actual pdf document based on minimal example. writer for tokens. bump language version
This commit is contained in:
@@ -4,7 +4,8 @@
|
|||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -4,11 +4,61 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Content;
|
using Content;
|
||||||
using PdfPig.Geometry;
|
using PdfPig.Geometry;
|
||||||
|
using PdfPig.Util;
|
||||||
using PdfPig.Writer;
|
using PdfPig.Writer;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
public class PdfDocumentBuilderTests
|
public class PdfDocumentBuilderTests
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CanWriteSingleBlankPage()
|
||||||
|
{
|
||||||
|
var result = CreateSingleBlankPage();
|
||||||
|
|
||||||
|
Assert.NotEmpty(result);
|
||||||
|
|
||||||
|
var str = OtherEncodings.BytesAsLatin1String(result);
|
||||||
|
Assert.StartsWith("%PDF", str);
|
||||||
|
Assert.EndsWith("%%EOF", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanReadSingleBlankPage()
|
||||||
|
{
|
||||||
|
var result = CreateSingleBlankPage();
|
||||||
|
|
||||||
|
using (var document = PdfDocument.Open(result, new ParsingOptions { UseLenientParsing = false }))
|
||||||
|
{
|
||||||
|
Assert.Equal(1, document.NumberOfPages);
|
||||||
|
|
||||||
|
var page = document.GetPage(1);
|
||||||
|
|
||||||
|
Assert.Equal(PageSize.A4, page.Size);
|
||||||
|
|
||||||
|
Assert.Empty(page.Letters);
|
||||||
|
|
||||||
|
Assert.NotNull(document.Structure.Catalog);
|
||||||
|
|
||||||
|
foreach (var offset in document.Structure.CrossReferenceTable.ObjectOffsets)
|
||||||
|
{
|
||||||
|
var obj = document.Structure.GetObject(offset.Key);
|
||||||
|
|
||||||
|
Assert.NotNull(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] CreateSingleBlankPage()
|
||||||
|
{
|
||||||
|
var builder = new PdfDocumentBuilder();
|
||||||
|
|
||||||
|
builder.AddPage(PageSize.A4);
|
||||||
|
|
||||||
|
var result = builder.Build();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CanWriteSinglePageHelloWorld()
|
public void CanWriteSinglePageHelloWorld()
|
||||||
{
|
{
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
<Product>PdfPig</Product>
|
<Product>PdfPig</Product>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
internal class PdfDocumentBuilder
|
internal class PdfDocumentBuilder
|
||||||
{
|
{
|
||||||
private static readonly byte Break = (byte) '\n';
|
private static readonly byte Break = (byte)'\n';
|
||||||
private static readonly TrueTypeFontParser Parser = new TrueTypeFontParser();
|
private static readonly TrueTypeFontParser Parser = new TrueTypeFontParser();
|
||||||
|
|
||||||
private readonly Dictionary<int, PdfPageBuilder> pages = new Dictionary<int, PdfPageBuilder>();
|
private readonly Dictionary<int, PdfPageBuilder> pages = new Dictionary<int, PdfPageBuilder>();
|
||||||
@@ -97,11 +97,12 @@
|
|||||||
// Body
|
// Body
|
||||||
foreach (var font in fonts)
|
foreach (var font in fonts)
|
||||||
{
|
{
|
||||||
var widths = new ArrayToken(new [] { new NumericToken(0), new NumericToken(255) });
|
var widths = new ArrayToken(new[] { new NumericToken(0), new NumericToken(255) });
|
||||||
var widthsObj = WriteObject(widths, memory, objectLocations, ref number);
|
var widthsObj = WriteObject(widths, memory, objectLocations, ref number);
|
||||||
|
|
||||||
var descriptorRef = new IndirectReference(number++, 0);
|
// TODO
|
||||||
|
// var descriptorRef = new IndirectReference(number++, 0);
|
||||||
|
|
||||||
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>
|
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>
|
||||||
{
|
{
|
||||||
{ NameToken.Type, NameToken.Font },
|
{ NameToken.Type, NameToken.Font },
|
||||||
@@ -110,29 +111,36 @@
|
|||||||
{ NameToken.LastChar, new NumericToken(255) },
|
{ NameToken.LastChar, new NumericToken(255) },
|
||||||
{ NameToken.Encoding, NameToken.WinAnsiEncoding },
|
{ NameToken.Encoding, NameToken.WinAnsiEncoding },
|
||||||
{ NameToken.Widths, widthsObj },
|
{ NameToken.Widths, widthsObj },
|
||||||
{ NameToken.FontDesc, new IndirectReferenceToken(descriptorRef) }
|
//{ NameToken.FontDesc, new IndirectReferenceToken(descriptorRef) }
|
||||||
});
|
});
|
||||||
|
|
||||||
var fontObj = WriteObject(dictionary, memory, objectLocations, ref number);
|
var fontObj = WriteObject(dictionary, memory, objectLocations, ref number);
|
||||||
fontsWritten.Add(font.Key, fontObj);
|
fontsWritten.Add(font.Key, fontObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var resources = new Dictionary<IToken, IToken>
|
||||||
|
{
|
||||||
|
{ NameToken.ProcSet, new ArrayToken(new []{ NameToken.Create("PDF"), NameToken.Create("Text") }) }
|
||||||
|
};
|
||||||
|
|
||||||
var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => ((IToken)fonts[x.Key].FontKey.Name, (IToken)new IndirectReferenceToken(x.Value.Number)))
|
if (fontsWritten.Count > 0)
|
||||||
.ToDictionary(x => x.Item1, x => x.Item2));
|
{
|
||||||
|
var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => ((IToken)fonts[x.Key].FontKey.Name, (IToken)new IndirectReferenceToken(x.Value.Number)))
|
||||||
|
.ToDictionary(x => x.Item1, x => x.Item2));
|
||||||
|
|
||||||
var fontsDictionaryRef = WriteObject(fontsDictionary, memory, objectLocations, ref number);
|
var fontsDictionaryRef = WriteObject(fontsDictionary, memory, objectLocations, ref number);
|
||||||
|
|
||||||
|
resources.Add(NameToken.Font, new IndirectReferenceToken(fontsDictionaryRef.Number));
|
||||||
|
}
|
||||||
|
|
||||||
var page = new DictionaryToken(new Dictionary<IToken, IToken>
|
var page = new DictionaryToken(new Dictionary<IToken, IToken>
|
||||||
{
|
{
|
||||||
{ NameToken.Type, NameToken.Page },
|
{ NameToken.Type, NameToken.Page },
|
||||||
{
|
{
|
||||||
NameToken.Resources,
|
NameToken.Resources,
|
||||||
new DictionaryToken(new Dictionary<IToken, IToken>
|
new DictionaryToken(resources)
|
||||||
{
|
},
|
||||||
{ NameToken.ProcSet, new ArrayToken(new []{ NameToken.Create("PDF"), NameToken.Create("Text") }) },
|
{ NameToken.MediaBox, RectangleToArray(pages[1].PageSize) }
|
||||||
{ NameToken.Font, new IndirectReferenceToken(fontsDictionaryRef.Number) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var pageRef = WriteObject(page, memory, objectLocations, ref number);
|
var pageRef = WriteObject(page, memory, objectLocations, ref number);
|
||||||
@@ -152,20 +160,32 @@
|
|||||||
{ NameToken.Pages, new IndirectReferenceToken(pagesRef.Number) }
|
{ NameToken.Pages, new IndirectReferenceToken(pagesRef.Number) }
|
||||||
});
|
});
|
||||||
|
|
||||||
WriteObject(catalog, memory, objectLocations, ref number);
|
var catalogRef = WriteObject(catalog, memory, objectLocations, ref number);
|
||||||
|
|
||||||
|
TokenWriter.WriteCrossReferenceTable(objectLocations, catalogRef, memory);
|
||||||
|
|
||||||
return memory.ToArray();
|
return memory.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectToken WriteObject(IToken content, MemoryStream stream, Dictionary<IndirectReference, long> objectOffsets, ref int number)
|
private static ArrayToken RectangleToArray(PdfRectangle rectangle)
|
||||||
|
{
|
||||||
|
return new ArrayToken(new[]
|
||||||
|
{
|
||||||
|
new NumericToken(rectangle.BottomLeft.X),
|
||||||
|
new NumericToken(rectangle.BottomLeft.Y),
|
||||||
|
new NumericToken(rectangle.TopRight.X),
|
||||||
|
new NumericToken(rectangle.TopRight.Y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ObjectToken WriteObject(IToken content, Stream stream, Dictionary<IndirectReference, long> objectOffsets, ref int number)
|
||||||
{
|
{
|
||||||
var reference = new IndirectReference(number++, 0);
|
var reference = new IndirectReference(number++, 0);
|
||||||
var obj = new ObjectToken(stream.Position, reference, content);
|
var obj = new ObjectToken(stream.Position, reference, content);
|
||||||
objectOffsets.Add(reference, obj.Position);
|
objectOffsets.Add(reference, obj.Position);
|
||||||
// TODO: write
|
// TODO: write
|
||||||
stream.Write(new byte[50], 0, 50);
|
TokenWriter.WriteToken(obj, stream);
|
||||||
stream.WriteByte(Break);
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +198,7 @@
|
|||||||
stream.WriteByte(Break);
|
stream.WriteByte(Break);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class FontStored
|
internal class FontStored
|
||||||
{
|
{
|
||||||
public AddedFont FontKey { get; }
|
public AddedFont FontKey { get; }
|
||||||
|
316
src/UglyToad.PdfPig/Writer/TokenWriter.cs
Normal file
316
src/UglyToad.PdfPig/Writer/TokenWriter.cs
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
namespace UglyToad.PdfPig.Writer
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Tokens;
|
||||||
|
using Util;
|
||||||
|
|
||||||
|
internal class TokenWriter
|
||||||
|
{
|
||||||
|
private static readonly byte ArrayStart = GetByte("[");
|
||||||
|
private static readonly byte ArrayEnd = GetByte("]");
|
||||||
|
|
||||||
|
private static readonly byte[] DictionaryStart = OtherEncodings.StringAsLatin1Bytes("<<");
|
||||||
|
private static readonly byte[] DictionaryEnd = OtherEncodings.StringAsLatin1Bytes(">>");
|
||||||
|
|
||||||
|
private static readonly byte Comment = GetByte("%");
|
||||||
|
|
||||||
|
private static readonly byte EndOfLine = OtherEncodings.StringAsLatin1Bytes("\n")[0];
|
||||||
|
|
||||||
|
private static readonly byte[] Eof = OtherEncodings.StringAsLatin1Bytes("%%EOF");
|
||||||
|
|
||||||
|
private static readonly byte[] FalseBytes = OtherEncodings.StringAsLatin1Bytes("false");
|
||||||
|
|
||||||
|
private static readonly byte InUseEntry = GetByte("n");
|
||||||
|
|
||||||
|
private static readonly byte NameStart = GetByte("/");
|
||||||
|
|
||||||
|
private static readonly byte[] Null = OtherEncodings.StringAsLatin1Bytes("null");
|
||||||
|
|
||||||
|
private static readonly byte[] ObjStart = OtherEncodings.StringAsLatin1Bytes("obj");
|
||||||
|
private static readonly byte[] ObjEnd = OtherEncodings.StringAsLatin1Bytes("endobj");
|
||||||
|
|
||||||
|
private static readonly byte RByte = GetByte("R");
|
||||||
|
|
||||||
|
private static readonly byte[] StartXref = OtherEncodings.StringAsLatin1Bytes("startxref");
|
||||||
|
|
||||||
|
private static readonly byte StringStart = GetByte("(");
|
||||||
|
private static readonly byte StringEnd = GetByte(")");
|
||||||
|
|
||||||
|
private static readonly byte[] Trailer = OtherEncodings.StringAsLatin1Bytes("trailer");
|
||||||
|
|
||||||
|
private static readonly byte[] TrueBytes = OtherEncodings.StringAsLatin1Bytes("true");
|
||||||
|
|
||||||
|
private static readonly byte Whitespace = GetByte(" ");
|
||||||
|
|
||||||
|
private static readonly byte[] Xref = OtherEncodings.StringAsLatin1Bytes("xref");
|
||||||
|
|
||||||
|
public static void WriteToken(IToken token, Stream outputStream)
|
||||||
|
{
|
||||||
|
switch (token)
|
||||||
|
{
|
||||||
|
case ArrayToken array:
|
||||||
|
WriteArray(array, outputStream);
|
||||||
|
break;
|
||||||
|
case BooleanToken boolean:
|
||||||
|
WriteBoolean(boolean, outputStream);
|
||||||
|
break;
|
||||||
|
case CommentToken comment:
|
||||||
|
WriteComment(comment, outputStream);
|
||||||
|
break;
|
||||||
|
case DictionaryToken dictionary:
|
||||||
|
WriteDictionary(dictionary, outputStream);
|
||||||
|
break;
|
||||||
|
case HexToken _:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
case IndirectReferenceToken reference:
|
||||||
|
WriteIndirectReference(reference, outputStream);
|
||||||
|
break;
|
||||||
|
case NameToken name:
|
||||||
|
WriteName(name, outputStream);
|
||||||
|
break;
|
||||||
|
case NullToken _:
|
||||||
|
outputStream.Write(Null, 0, Null.Length);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
break;
|
||||||
|
case NumericToken number:
|
||||||
|
WriteNumber(number, outputStream);
|
||||||
|
break;
|
||||||
|
case ObjectToken objectToken:
|
||||||
|
WriteObject(objectToken, outputStream);
|
||||||
|
break;
|
||||||
|
case StreamToken _:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
case StringToken stringToken:
|
||||||
|
WriteString(stringToken, outputStream);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteCrossReferenceTable(IReadOnlyDictionary<IndirectReference, long> objectOffsets,
|
||||||
|
ObjectToken catalogToken,
|
||||||
|
Stream outputStream)
|
||||||
|
{
|
||||||
|
if (objectOffsets.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Could not write empty cross reference table.");
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
var position = outputStream.Position;
|
||||||
|
outputStream.Write(Xref, 0, Xref.Length);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
|
||||||
|
var min = objectOffsets.Min(x => x.Key.ObjectNumber);
|
||||||
|
var max = objectOffsets.Max(x => x.Key.ObjectNumber);
|
||||||
|
|
||||||
|
if (max - min != objectOffsets.Count - 1)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Object numbers must form a contiguous range");
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLong(min, outputStream);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
WriteLong(max, outputStream);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
|
||||||
|
foreach (var keyValuePair in objectOffsets.OrderBy(x => x.Key.ObjectNumber))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* nnnnnnnnnn ggggg n eol
|
||||||
|
* where:
|
||||||
|
* nnnnnnnnnn is a 10-digit byte offset
|
||||||
|
* ggggg is a 5-digit generation number
|
||||||
|
* n is a literal keyword identifying this as an in-use entry
|
||||||
|
* eol is a 2-character end-of-line sequence ('\r\n' or ' \n')
|
||||||
|
*/
|
||||||
|
var paddedOffset = OtherEncodings.StringAsLatin1Bytes(keyValuePair.Value.ToString("D10"));
|
||||||
|
outputStream.Write(paddedOffset, 0, paddedOffset.Length);
|
||||||
|
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
|
||||||
|
var generation = OtherEncodings.StringAsLatin1Bytes(keyValuePair.Key.Generation.ToString("D5"));
|
||||||
|
outputStream.Write(generation, 0, generation.Length);
|
||||||
|
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
|
||||||
|
outputStream.WriteByte(InUseEntry);
|
||||||
|
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.Write(Trailer, 0, Trailer.Length);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
|
||||||
|
var trailerDictionary = new DictionaryToken(new Dictionary<IToken, IToken>
|
||||||
|
{
|
||||||
|
{NameToken.Size, new NumericToken(objectOffsets.Count) },
|
||||||
|
{NameToken.Root, new IndirectReferenceToken(catalogToken.Number) }
|
||||||
|
});
|
||||||
|
|
||||||
|
WriteDictionary(trailerDictionary, outputStream);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
|
||||||
|
outputStream.Write(StartXref, 0, StartXref.Length);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
|
||||||
|
WriteLong(position, outputStream);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
|
||||||
|
// Complete!
|
||||||
|
outputStream.Write(Eof, 0, Eof.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteArray(ArrayToken array, Stream outputStream)
|
||||||
|
{
|
||||||
|
outputStream.WriteByte(ArrayStart);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
|
||||||
|
for (var i = 0; i < array.Data.Count; i++)
|
||||||
|
{
|
||||||
|
var value = array.Data[i];
|
||||||
|
WriteToken(value, outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.WriteByte(ArrayEnd);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static 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)
|
||||||
|
{
|
||||||
|
var bytes = OtherEncodings.StringAsLatin1Bytes(comment.Data);
|
||||||
|
outputStream.WriteByte(Comment);
|
||||||
|
outputStream.Write(bytes, 0, bytes.Length);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteDictionary(DictionaryToken dictionary, Stream outputStream)
|
||||||
|
{
|
||||||
|
outputStream.Write(DictionaryStart, 0, DictionaryStart.Length);
|
||||||
|
|
||||||
|
foreach (var pair in dictionary.Data)
|
||||||
|
{
|
||||||
|
WriteName(pair.Key, outputStream);
|
||||||
|
WriteToken(pair.Value, outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.Write(DictionaryEnd, 0, DictionaryEnd.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteIndirectReference(IndirectReferenceToken reference, Stream outputStream)
|
||||||
|
{
|
||||||
|
WriteLong(reference.Data.ObjectNumber, outputStream);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
|
||||||
|
WriteInt(reference.Data.Generation, outputStream);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
|
||||||
|
outputStream.WriteByte(RByte);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteName(NameToken name, Stream outputStream)
|
||||||
|
{
|
||||||
|
WriteName(name.Data, outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteName(string name, Stream outputStream)
|
||||||
|
{
|
||||||
|
var bytes = OtherEncodings.StringAsLatin1Bytes(name);
|
||||||
|
|
||||||
|
outputStream.WriteByte(NameStart);
|
||||||
|
outputStream.Write(bytes, 0, bytes.Length);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteNumber(NumericToken number, Stream outputStream)
|
||||||
|
{
|
||||||
|
if (!number.HasDecimalPlaces)
|
||||||
|
{
|
||||||
|
WriteInt(number.Int, outputStream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var bytes = OtherEncodings.StringAsLatin1Bytes(number.Data.ToString("G"));
|
||||||
|
outputStream.Write(bytes, 0, bytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteObject(ObjectToken objectToken, Stream outputStream)
|
||||||
|
{
|
||||||
|
WriteLong(objectToken.Number.ObjectNumber, outputStream);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
|
||||||
|
WriteInt(objectToken.Number.Generation, outputStream);
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
|
||||||
|
outputStream.Write(ObjStart, 0, ObjStart.Length);
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
|
||||||
|
WriteToken(objectToken.Data, outputStream);
|
||||||
|
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
outputStream.Write(ObjEnd, 0, ObjEnd.Length);
|
||||||
|
|
||||||
|
WriteLineBreak(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteString(StringToken stringToken, Stream outputStream)
|
||||||
|
{
|
||||||
|
outputStream.WriteByte(StringStart);
|
||||||
|
var bytes = OtherEncodings.StringAsLatin1Bytes(stringToken.Data);
|
||||||
|
outputStream.Write(bytes, 0, bytes.Length);
|
||||||
|
outputStream.WriteByte(StringEnd);
|
||||||
|
|
||||||
|
WriteWhitespace(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteInt(int value, Stream outputStream)
|
||||||
|
{
|
||||||
|
var bytes = OtherEncodings.StringAsLatin1Bytes(value.ToString("G"));
|
||||||
|
outputStream.Write(bytes, 0, bytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteLineBreak(Stream outputStream)
|
||||||
|
{
|
||||||
|
outputStream.WriteByte(EndOfLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteLong(long value, Stream outputStream)
|
||||||
|
{
|
||||||
|
var bytes = OtherEncodings.StringAsLatin1Bytes(value.ToString("G"));
|
||||||
|
outputStream.Write(bytes, 0, bytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteWhitespace(Stream outputStream)
|
||||||
|
{
|
||||||
|
outputStream.WriteByte(Whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte GetByte(string value)
|
||||||
|
{
|
||||||
|
var bytes = OtherEncodings.StringAsLatin1Bytes(value);
|
||||||
|
|
||||||
|
if (bytes.Length > 1)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user