Skip to content

Commit

Permalink
Fix #878
Browse files Browse the repository at this point in the history
  • Loading branch information
BobLd committed Sep 24, 2024
1 parent 4845f43 commit 084fa4e
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 11 deletions.
231 changes: 231 additions & 0 deletions src/UglyToad.PdfPig.Tests/Writer/OperationWriteHelperTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
namespace UglyToad.PdfPig.Tests.Writer
{
using UglyToad.PdfPig.Graphics.Operations;

public class OperationWriteHelperTests
{
[Fact]
public void WriteDouble0()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, 0);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("0", line);
}
}
}

[Fact]
public void WriteDouble5()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, 5);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("5", line);
}
}
}

[Fact]
public void WriteDoubleMinus5()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, -5);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("-5", line);
}
}
}

[Fact]
public void WriteDouble10()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, 10);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("10", line);
}
}
}

[Fact]
public void WriteDoubleMinus10()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, -10);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("-10", line);
}
}
}

[Fact]
public void WriteDouble1()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, 0.00000001);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("0.00000001", line);
}
}
}

[Fact]
public void WriteDouble1bis()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, -0.00000001);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("-0.00000001", line);
}
}
}

[Fact]
public void WriteDouble2()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, .00000005100);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("0.000000051", line);
}
}
}

[Fact]
public void WriteDouble2bis()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, -.0000000510);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("-0.000000051", line);
}
}
}

[Fact]
public void WriteDouble3()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, 15001.98);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
var v = double.Parse(line);
Assert.Equal(15001.98, v);
}
}
}

[Fact]
public void WriteDouble4()
{
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, 10000.000);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal("10000", line);
}
}
}

[Fact]
public void WriteDoubleMinValue()
{
string expected = "-340282346638528859811704183484516925440";
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, -340282346638528859811704183484516925440d);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal(expected, line);
}
}
}

[Fact]
public void WriteDoubleMaxValue()
{
string expected = "340282346638528859811704183484516925440";
using (var memStream = new MemoryStream())
{
OperationWriteHelper.WriteDouble(memStream, 340282346638528859811704183484516925440d);

// Read Test
memStream.Position = 0;
using (var streamReader = new StreamReader(memStream))
{
var line = streamReader.ReadToEnd();
Assert.Equal(expected, line);
}
}
}
}
}
63 changes: 57 additions & 6 deletions src/UglyToad.PdfPig/Graphics/Operations/OperationWriteHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,31 @@
{
using PdfPig.Core;
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Globalization;
using System.IO;
using System.Text;
using Util;

internal static class OperationWriteHelper
{
private const byte Whitespace = (byte)' ';
private const byte NewLine = (byte)'\n';
private const byte Zero = (byte)'0';
private const byte Point = (byte)'.';

private static readonly StandardFormat StandardFormatDouble = new StandardFormat('F', 9);

public static void WriteText(this Stream stream, string text, bool appendWhitespace = false)
{
#if NET8_0_OR_GREATER
if (Ascii.IsValid(text))
if (System.Text.Ascii.IsValid(text))
{
Span<byte> buffer = text.Length <= 64
? stackalloc byte[text.Length]
: new byte[text.Length];

Ascii.FromUtf16(text, buffer, out _);
System.Text.Ascii.FromUtf16(text, buffer, out _);

stream.Write(buffer);
}
Expand Down Expand Up @@ -75,11 +80,57 @@ public static void WriteNewLine(this Stream stream)

public static void WriteDouble(this Stream stream, double value)
{
Span<byte> buffer = stackalloc byte[32]; // matches dotnet Number.CharStackBufferSize
int stackSize = 32; // matches dotnet Number.CharStackBufferSize

bool success = TryWriteDouble(stream, value, stackSize);
while (!success && stackSize <= 1024)
{
stackSize *= 2;
success = TryWriteDouble(stream, value, stackSize);
}

if (!success)
{
ReadOnlySpan<byte> buffer = System.Text.Encoding.UTF8.GetBytes(value.ToString("F9", CultureInfo.InvariantCulture));
int lastIndex = GetLastSignificantDigitIndex(buffer, buffer.Length);
stream.Write(buffer.Slice(0, lastIndex));
}
}

private static bool TryWriteDouble(Stream stream, double value, int stackSize)
{
System.Diagnostics.Debug.Assert(stackSize <= 1024);

Span<byte> buffer = stackalloc byte[stackSize];

if (Utf8Formatter.TryFormat(value, buffer, out int bytesWritten, StandardFormatDouble))
{
int lastIndex = GetLastSignificantDigitIndex(buffer, bytesWritten);
stream.Write(buffer.Slice(0, lastIndex));
return true;
}

return false;
}

private static int GetLastSignificantDigitIndex(ReadOnlySpan<byte> buffer, int bytesWritten)
{
int lastIndex = bytesWritten;
for (int i = bytesWritten - 1; i > 1; --i)
{
if (buffer[i] != Zero)
{
break;
}
lastIndex--;
}

Utf8Formatter.TryFormat(value, buffer, out int bytesWritten);
if (buffer[lastIndex - 1] == Point)
{
lastIndex--;
}

stream.Write(buffer.Slice(0, bytesWritten));
return lastIndex;
}

public static void WriteNumberText(this Stream stream, int number, string text)
Expand Down
6 changes: 1 addition & 5 deletions src/UglyToad.PdfPig/Writer/TokenWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -461,11 +461,7 @@ protected virtual void WriteNumber(NumericToken number, Stream outputStream)
}
else
{
Span<byte> buffer = stackalloc byte[32]; // matches dotnet Number.CharStackBufferSize

Utf8Formatter.TryFormat(number.Data, buffer, out int bytesWritten);

outputStream.Write(buffer.Slice(0, bytesWritten));
outputStream.WriteDouble(number.Data);
}

WriteWhitespace(outputStream);
Expand Down

0 comments on commit 084fa4e

Please sign in to comment.