mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-11-24 08:47:01 +08:00
Fix #878
This commit is contained in:
236
src/UglyToad.PdfPig.Tests/Writer/OperationWriteHelperTests.cs
Normal file
236
src/UglyToad.PdfPig.Tests/Writer/OperationWriteHelperTests.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
namespace UglyToad.PdfPig.Tests.Writer
|
||||
{
|
||||
using System.Globalization;
|
||||
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, CultureInfo.InvariantCulture);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if NET
|
||||
// See here why we are not running on framework - thanks @cremor
|
||||
// https://stackoverflow.com/a/1658420/631802
|
||||
[Fact]
|
||||
public void WriteMinValue()
|
||||
{
|
||||
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 WriteMaxValue()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -75,11 +80,57 @@
|
||||
|
||||
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
|
||||
|
||||
Utf8Formatter.TryFormat(value, buffer, out int bytesWritten);
|
||||
bool success = TryWriteDouble(stream, value, stackSize);
|
||||
while (!success && stackSize <= 1024)
|
||||
{
|
||||
stackSize *= 2;
|
||||
success = TryWriteDouble(stream, value, stackSize);
|
||||
}
|
||||
|
||||
stream.Write(buffer.Slice(0, bytesWritten));
|
||||
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--;
|
||||
}
|
||||
|
||||
if (buffer[lastIndex - 1] == Point)
|
||||
{
|
||||
lastIndex--;
|
||||
}
|
||||
|
||||
return lastIndex;
|
||||
}
|
||||
|
||||
public static void WriteNumberText(this Stream stream, int number, string text)
|
||||
|
||||
@@ -461,11 +461,7 @@
|
||||
}
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user