diff --git a/src/UglyToad.PdfPig.Core/PdfRectangle.cs b/src/UglyToad.PdfPig.Core/PdfRectangle.cs
index eef71ec53..f5661b509 100644
--- a/src/UglyToad.PdfPig.Core/PdfRectangle.cs
+++ b/src/UglyToad.PdfPig.Core/PdfRectangle.cs
@@ -16,22 +16,22 @@ public struct PdfRectangle
///
/// Top left point of the rectangle.
///
- public PdfPoint TopLeft { get; }
+ public readonly PdfPoint TopLeft { get; }
///
/// Top right point of the rectangle.
///
- public PdfPoint TopRight { get; }
+ public readonly PdfPoint TopRight { get; }
///
/// Bottom right point of the rectangle.
///
- public PdfPoint BottomRight { get; }
+ public readonly PdfPoint BottomRight { get; }
///
/// Bottom left point of the rectangle.
///
- public PdfPoint BottomLeft { get; }
+ public readonly PdfPoint BottomLeft { get; }
///
/// Centroid point of the rectangle.
diff --git a/src/UglyToad.PdfPig.Core/ReadHelper.cs b/src/UglyToad.PdfPig.Core/ReadHelper.cs
index 07a07e076..a40a37250 100644
--- a/src/UglyToad.PdfPig.Core/ReadHelper.cs
+++ b/src/UglyToad.PdfPig.Core/ReadHelper.cs
@@ -1,8 +1,8 @@
namespace UglyToad.PdfPig.Core
{
using System;
+ using System.Buffers.Text;
using System.Collections.Generic;
- using System.Globalization;
using System.Text;
#if NET8_0_OR_GREATER
@@ -41,8 +41,6 @@ public static class ReadHelper
'\f'
];
- private static readonly int MaximumNumberStringLength = long.MaxValue.ToString("D").Length;
-
///
/// Read a string from the input until a newline.
///
@@ -134,7 +132,7 @@ public static bool IsEndOfName(int ch)
///
public static bool IsWhitespace(byte c)
{
- return c == 0 || c == 32 || c == AsciiLineFeed || c == AsciiCarriageReturn || c == 9 || c == 12;
+ return c is 0 or 32 or AsciiLineFeed or AsciiCarriageReturn or 9 or 12;
}
///
@@ -198,25 +196,24 @@ public static bool IsString(IInputBytes bytes, string s)
public static long ReadLong(IInputBytes bytes)
{
SkipSpaces(bytes);
- long retval;
- StringBuilder longBuffer = ReadStringNumber(bytes);
+ Span buffer = stackalloc byte[19]; // max formatted uint64 length
- try
+ ReadNumberAsUtf8Bytes(bytes, buffer, out int bytesRead);
+
+ ReadOnlySpan longBytes = buffer.Slice(0, bytesRead);
+
+ if (Utf8Parser.TryParse(longBytes, out long result, out _))
{
- retval = long.Parse(longBuffer.ToString(), CultureInfo.InvariantCulture);
+ return result;
}
- catch (FormatException e)
+ else
{
- var bytesToReverse = OtherEncodings.StringAsLatin1Bytes(longBuffer.ToString());
- bytes.Seek(bytes.CurrentOffset - bytesToReverse.Length);
+ bytes.Seek(bytes.CurrentOffset - bytesRead);
- throw new InvalidOperationException($"Error: Expected a long type at offset {bytes.CurrentOffset}, instead got \'{longBuffer}\'", e);
+ throw new InvalidOperationException($"Error: Expected a long type at offset {bytes.CurrentOffset}, instead got \'{OtherEncodings.BytesAsLatin1String(longBytes)}\'");
}
-
- return retval;
}
-
///
/// Whether the given value is a digit or not.
@@ -231,28 +228,29 @@ public static bool IsDigit(int c)
///
public static int ReadInt(IInputBytes bytes)
{
- if (bytes == null)
+ if (bytes is null)
{
throw new ArgumentNullException(nameof(bytes));
}
SkipSpaces(bytes);
- int result;
- var intBuffer = ReadStringNumber(bytes);
+ Span buffer = stackalloc byte[10]; // max formatted uint32 length
- try
+ ReadNumberAsUtf8Bytes(bytes, buffer, out int bytesRead);
+
+ var intBytes = buffer.Slice(0, bytesRead);
+
+ if (Utf8Parser.TryParse(intBytes, out int result, out _))
{
- result = int.Parse(intBuffer.ToString(), CultureInfo.InvariantCulture);
+ return result;
}
- catch (Exception e)
+ else
{
- bytes.Seek(bytes.CurrentOffset - OtherEncodings.StringAsLatin1Bytes(intBuffer.ToString()).Length);
-
- throw new PdfDocumentFormatException($"Error: Expected an integer type at offset {bytes.CurrentOffset}", e);
+ bytes.Seek(bytes.CurrentOffset - bytesRead);
+
+ throw new PdfDocumentFormatException($"Error: Expected an integer type at offset {bytes.CurrentOffset}, instead got \'{OtherEncodings.BytesAsLatin1String(intBytes)}\'");
}
-
- return result;
}
///
@@ -304,25 +302,26 @@ public static bool IsValidUtf8(byte[] input)
#endif
}
- private static StringBuilder ReadStringNumber(IInputBytes reader)
+ private static void ReadNumberAsUtf8Bytes(IInputBytes reader, scoped Span buffer, out int bytesRead)
{
- byte lastByte;
- StringBuilder buffer = new StringBuilder();
+ int position = 0;
+ byte lastByte;
+
while (reader.MoveNext() && (lastByte = reader.CurrentByte) != ' ' &&
lastByte != AsciiLineFeed &&
lastByte != AsciiCarriageReturn &&
- lastByte != 60 && //see sourceforge bug 1714707
+ lastByte != 60 && // see sourceforge bug 1714707
lastByte != '[' && // PDFBOX-1845
lastByte != '(' && // PDFBOX-2579
lastByte != 0)
{
- buffer.Append((char)lastByte);
-
- if (buffer.Length > MaximumNumberStringLength)
+ if (position >= buffer.Length)
{
- throw new InvalidOperationException($"Number \'{buffer}\' is getting too long, stop reading at offset {reader.CurrentOffset}");
+ throw new InvalidOperationException($"Number \'{OtherEncodings.BytesAsLatin1String(buffer.Slice(0, position))}\' is getting too long, stop reading at offset {reader.CurrentOffset}");
}
+
+ buffer[position++] = lastByte;
}
if (!reader.IsAtEnd())
@@ -330,7 +329,7 @@ private static StringBuilder ReadStringNumber(IInputBytes reader)
reader.Seek(reader.CurrentOffset - 1);
}
- return buffer;
+ bytesRead = position;
}
}
}
diff --git a/src/UglyToad.PdfPig.Fonts/AdobeFontMetrics/AdobeFontMetricsCharacterSize.cs b/src/UglyToad.PdfPig.Fonts/AdobeFontMetrics/AdobeFontMetricsCharacterSize.cs
index df4c4d7e7..c1be1a5bf 100644
--- a/src/UglyToad.PdfPig.Fonts/AdobeFontMetrics/AdobeFontMetricsCharacterSize.cs
+++ b/src/UglyToad.PdfPig.Fonts/AdobeFontMetrics/AdobeFontMetricsCharacterSize.cs
@@ -4,7 +4,7 @@
/// The x and y components of the width vector of the font's characters.
/// Presence implies that IsFixedPitch is true.
///
- public class AdobeFontMetricsCharacterSize
+ public readonly struct AdobeFontMetricsCharacterSize
{
///
/// The horizontal width.
diff --git a/src/UglyToad.PdfPig.Fonts/AdobeFontMetrics/AdobeFontMetricsLigature.cs b/src/UglyToad.PdfPig.Fonts/AdobeFontMetrics/AdobeFontMetricsLigature.cs
index 93e524479..b64817ea9 100644
--- a/src/UglyToad.PdfPig.Fonts/AdobeFontMetrics/AdobeFontMetricsLigature.cs
+++ b/src/UglyToad.PdfPig.Fonts/AdobeFontMetrics/AdobeFontMetricsLigature.cs
@@ -3,7 +3,7 @@
///
/// A ligature in an Adobe Font Metrics individual character.
///
- public class AdobeFontMetricsLigature
+ public readonly struct AdobeFontMetricsLigature
{
///
/// The character to join with to form a ligature.
diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatData.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatData.cs
index b28b6248b..a4441905f 100644
--- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatData.cs
+++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatData.cs
@@ -1,10 +1,8 @@
namespace UglyToad.PdfPig.Fonts.CompactFontFormat
{
using System;
- using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
- using Core;
///
/// Provides access to the raw bytes of this Compact Font Format file with utility methods for reading data types from it.
@@ -37,14 +35,7 @@ public CompactFontFormatData(ReadOnlyMemory dataBytes)
///
public string ReadString(int length, Encoding encoding)
{
- var bytes = new byte[length];
-
- for (var i = 0; i < bytes.Length; i++)
- {
- bytes[i] = ReadByte();
- }
-
- return encoding.GetString(bytes);
+ return encoding.GetString(ReadSpan(length));
}
///
@@ -86,6 +77,20 @@ public int ReadOffset(int offsetSize)
return value;
}
+ internal ReadOnlySpan ReadSpan(int count)
+ {
+ if (Position + count >= dataBytes.Length)
+ {
+ throw new IndexOutOfRangeException($"Cannot read past end of data. Attempted to read to {Position + count} when the underlying data is {dataBytes.Length} bytes long.");
+ }
+
+ var result = dataBytes.Span.Slice(Position + 1, count);
+
+ Position += count;
+
+ return result;
+ }
+
///
/// Read byte.
///
diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatEncodingReader.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatEncodingReader.cs
index 351276a1d..b3020b70f 100644
--- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatEncodingReader.cs
+++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatEncodingReader.cs
@@ -8,7 +8,7 @@
internal static class CompactFontFormatEncodingReader
{
- public static Encoding ReadEncoding(CompactFontFormatData data, ICompactFontFormatCharset charset, IReadOnlyList stringIndex)
+ public static Encoding ReadEncoding(CompactFontFormatData data, ICompactFontFormatCharset charset, ReadOnlySpan stringIndex)
{
if (data == null)
{
@@ -32,7 +32,7 @@ public static Encoding ReadEncoding(CompactFontFormatData data, ICompactFontForm
}
}
- private static CompactFontFormatFormat0Encoding ReadFormat0Encoding(CompactFontFormatData data, ICompactFontFormatCharset charset, IReadOnlyList stringIndex, byte format)
+ private static CompactFontFormatFormat0Encoding ReadFormat0Encoding(CompactFontFormatData data, ICompactFontFormatCharset charset, ReadOnlySpan stringIndex, byte format)
{
var numberOfCodes = data.ReadCard8();
@@ -45,7 +45,7 @@ private static CompactFontFormatFormat0Encoding ReadFormat0Encoding(CompactFontF
values.Add((code, sid, str));
}
- IReadOnlyList supplements = new List();
+ IReadOnlyList supplements = [];
if (HasSupplement(format))
{
supplements = ReadSupplement(data, stringIndex);
@@ -54,7 +54,7 @@ private static CompactFontFormatFormat0Encoding ReadFormat0Encoding(CompactFontF
return new CompactFontFormatFormat0Encoding(values, supplements);
}
- private static CompactFontFormatFormat1Encoding ReadFormat1Encoding(CompactFontFormatData data, ICompactFontFormatCharset charset, IReadOnlyList stringIndex, byte format)
+ private static CompactFontFormatFormat1Encoding ReadFormat1Encoding(CompactFontFormatData data, ICompactFontFormatCharset charset, ReadOnlySpan stringIndex, byte format)
{
var numberOfRanges = data.ReadCard8();
@@ -85,7 +85,7 @@ private static CompactFontFormatFormat1Encoding ReadFormat1Encoding(CompactFontF
}
private static IReadOnlyList ReadSupplement(CompactFontFormatData dataInput,
- IReadOnlyList stringIndex)
+ ReadOnlySpan stringIndex)
{
var numberOfSupplements = dataInput.ReadCard8();
var supplements = new CompactFontFormatBuiltInEncoding.Supplement[numberOfSupplements];
@@ -101,13 +101,13 @@ private static CompactFontFormatFormat1Encoding ReadFormat1Encoding(CompactFontF
return supplements;
}
- private static string ReadString(int index, IReadOnlyList stringIndex)
+ private static string ReadString(int index, ReadOnlySpan stringIndex)
{
if (index >= 0 && index <= 390)
{
return CompactFontFormatStandardStrings.GetName(index);
}
- if (index - 391 < stringIndex.Count)
+ if (index - 391 < stringIndex.Length)
{
return stringIndex[index - 391];
}
diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatIndividualFontParser.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatIndividualFontParser.cs
index 843c2ef67..bd26eb172 100644
--- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatIndividualFontParser.cs
+++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/CompactFontFormatIndividualFontParser.cs
@@ -2,7 +2,6 @@
{
using System;
using System.Collections.Generic;
- using System.Linq;
using Charsets;
using CharStrings;
using Core;
@@ -23,7 +22,7 @@ public CompactFontFormatIndividualFontParser(CompactFontFormatTopLevelDictionary
this.privateDictionaryReader = privateDictionaryReader;
}
- public CompactFontFormatFont Parse(CompactFontFormatData data, string name, ReadOnlySpan topDictionaryIndex, IReadOnlyList stringIndex,
+ public CompactFontFormatFont Parse(CompactFontFormatData data, string name, ReadOnlySpan topDictionaryIndex, ReadOnlySpan stringIndex,
CompactFontFormatIndex globalSubroutineIndex)
{
var individualData = new CompactFontFormatData(topDictionaryIndex.ToArray());
@@ -127,8 +126,10 @@ public CompactFontFormatFont Parse(CompactFontFormatData data, string name, Read
return new CompactFontFormatFont(topDictionary, privateDictionary, charset, Union.Two(charStrings), fontEncoding);
}
- private static ICompactFontFormatCharset ReadCharset(CompactFontFormatData data, CompactFontFormatTopLevelDictionary topDictionary,
- CompactFontFormatIndex charStringIndex, IReadOnlyList stringIndex)
+ private static ICompactFontFormatCharset ReadCharset(CompactFontFormatData data,
+ CompactFontFormatTopLevelDictionary topDictionary,
+ CompactFontFormatIndex charStringIndex,
+ ReadOnlySpan stringIndex)
{
data.Seek(topDictionary.CharSetOffset);
@@ -180,13 +181,13 @@ private static ICompactFontFormatCharset ReadCharset(CompactFontFormatData data,
}
}
- private static string ReadString(int index, IReadOnlyList stringIndex)
+ private static string ReadString(int index, ReadOnlySpan stringIndex)
{
if (index >= 0 && index <= 390)
{
return CompactFontFormatStandardStrings.GetName(index);
}
- if (index - 391 < stringIndex.Count)
+ if (index - 391 < stringIndex.Length)
{
return stringIndex[index - 391];
}
@@ -213,9 +214,10 @@ private static Type2CharStrings ReadCharStrings(CompactFontFormatData data, Comp
}
}
- private CompactFontFormatCidFont ReadCidFont(CompactFontFormatData data, CompactFontFormatTopLevelDictionary topLevelDictionary,
+ private CompactFontFormatCidFont ReadCidFont(CompactFontFormatData data,
+ CompactFontFormatTopLevelDictionary topLevelDictionary,
int numberOfGlyphs,
- IReadOnlyList stringIndex,
+ ReadOnlySpan stringIndex,
CompactFontFormatPrivateDictionary privateDictionary,
ICompactFontFormatCharset charset,
CompactFontFormatIndex globalSubroutines,
diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatDictionaryReader.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatDictionaryReader.cs
index eeb84521f..9ee4f012a 100644
--- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatDictionaryReader.cs
+++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatDictionaryReader.cs
@@ -10,9 +10,9 @@ internal abstract class CompactFontFormatDictionaryReader
{
private readonly List operands = new List();
- public abstract TResult Read(CompactFontFormatData data, IReadOnlyList stringIndex);
+ public abstract TResult Read(CompactFontFormatData data, ReadOnlySpan stringIndex);
- protected TBuilder ReadDictionary(TBuilder builder, CompactFontFormatData data, IReadOnlyList stringIndex)
+ protected TBuilder ReadDictionary(TBuilder builder, CompactFontFormatData data, ReadOnlySpan stringIndex)
{
while (data.CanRead())
{
@@ -124,7 +124,7 @@ private static double ReadRealNumber(CompactFontFormatData data)
exponentMissing = false;
break;
case 0xa:
- sb.Append(".");
+ sb.Append('.');
break;
case 0xb:
if (hasExponent)
@@ -132,7 +132,7 @@ private static double ReadRealNumber(CompactFontFormatData data)
// avoid duplicates
break;
}
- sb.Append("E");
+ sb.Append('E');
exponentMissing = true;
hasExponent = true;
break;
@@ -149,7 +149,7 @@ private static double ReadRealNumber(CompactFontFormatData data)
case 0xd:
break;
case 0xe:
- sb.Append("-");
+ sb.Append('-');
break;
case 0xf:
done = true;
@@ -165,7 +165,7 @@ private static double ReadRealNumber(CompactFontFormatData data)
// the exponent is missing, just append "0" to avoid an exception
// not sure if 0 is the correct value, but it seems to fit
// see PDFBOX-1522
- sb.Append("0");
+ sb.Append('0');
}
if (sb.Length == 0)
@@ -176,9 +176,9 @@ private static double ReadRealNumber(CompactFontFormatData data)
return hasExponent ? double.Parse(sb.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture) : double.Parse(sb.ToString(), CultureInfo.InvariantCulture);
}
- protected abstract void ApplyOperation(TBuilder builder, List operands, OperandKey operandKey, IReadOnlyList stringIndex);
+ protected abstract void ApplyOperation(TBuilder builder, List operands, OperandKey operandKey, ReadOnlySpan stringIndex);
- protected static string GetString(List operands, IReadOnlyList stringIndex)
+ protected static string GetString(List operands, ReadOnlySpan stringIndex)
{
if (operands.Count == 0)
{
@@ -198,7 +198,7 @@ protected static string GetString(List operands, IReadOnlyList
}
var stringIndexIndex = index - 391;
- if (stringIndexIndex >= 0 && stringIndexIndex < stringIndex.Count)
+ if (stringIndexIndex >= 0 && stringIndexIndex < stringIndex.Length)
{
return stringIndex[stringIndexIndex];
}
diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatPrivateDictionaryReader.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatPrivateDictionaryReader.cs
index b6a3cb04a..9ef681506 100644
--- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatPrivateDictionaryReader.cs
+++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatPrivateDictionaryReader.cs
@@ -5,7 +5,7 @@
internal class CompactFontFormatPrivateDictionaryReader : CompactFontFormatDictionaryReader
{
- public override CompactFontFormatPrivateDictionary Read(CompactFontFormatData data, IReadOnlyList stringIndex)
+ public override CompactFontFormatPrivateDictionary Read(CompactFontFormatData data, ReadOnlySpan stringIndex)
{
var builder = new CompactFontFormatPrivateDictionary.Builder();
@@ -14,7 +14,7 @@ public override CompactFontFormatPrivateDictionary Read(CompactFontFormatData da
return builder.Build();
}
- protected override void ApplyOperation(CompactFontFormatPrivateDictionary.Builder dictionary, List operands, OperandKey operandKey, IReadOnlyList stringIndex)
+ protected override void ApplyOperation(CompactFontFormatPrivateDictionary.Builder dictionary, List operands, OperandKey operandKey, ReadOnlySpan stringIndex)
{
switch (operandKey.Byte0)
{
diff --git a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatTopLevelDictionaryReader.cs b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatTopLevelDictionaryReader.cs
index a07172b56..411703456 100644
--- a/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatTopLevelDictionaryReader.cs
+++ b/src/UglyToad.PdfPig.Fonts/CompactFontFormat/Dictionaries/CompactFontFormatTopLevelDictionaryReader.cs
@@ -6,7 +6,7 @@
internal class CompactFontFormatTopLevelDictionaryReader : CompactFontFormatDictionaryReader
{
- public override CompactFontFormatTopLevelDictionary Read(CompactFontFormatData data, IReadOnlyList stringIndex)
+ public override CompactFontFormatTopLevelDictionary Read(CompactFontFormatData data, ReadOnlySpan stringIndex)
{
var dictionary = new CompactFontFormatTopLevelDictionary();
@@ -15,7 +15,7 @@ public override CompactFontFormatTopLevelDictionary Read(CompactFontFormatData d
return dictionary;
}
- protected override void ApplyOperation(CompactFontFormatTopLevelDictionary dictionary, List operands, OperandKey key, IReadOnlyList stringIndex)
+ protected override void ApplyOperation(CompactFontFormatTopLevelDictionary dictionary, List operands, OperandKey key, ReadOnlySpan stringIndex)
{
switch (key.Byte0)
{
diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/DifferenceBasedEncoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/DifferenceBasedEncoding.cs
index 9493ebb7b..f6cfdbe05 100644
--- a/src/UglyToad.PdfPig.Fonts/Encodings/DifferenceBasedEncoding.cs
+++ b/src/UglyToad.PdfPig.Fonts/Encodings/DifferenceBasedEncoding.cs
@@ -8,7 +8,7 @@
///
/// Created by combining a base encoding with the differences.
///
- public class DifferenceBasedEncoding : Encoding
+ public sealed class DifferenceBasedEncoding : Encoding
{
///
public override string EncodingName { get; } = "Difference Encoding";
diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/MacExpertEncoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/MacExpertEncoding.cs
index c8ea61250..608c7df0d 100644
--- a/src/UglyToad.PdfPig.Fonts/Encodings/MacExpertEncoding.cs
+++ b/src/UglyToad.PdfPig.Fonts/Encodings/MacExpertEncoding.cs
@@ -1,8 +1,6 @@
namespace UglyToad.PdfPig.Fonts.Encodings
{
- using Core;
-
- internal class MacExpertEncoding : Encoding
+ internal sealed class MacExpertEncoding : Encoding
{
///
/// Table of octal character codes and their corresponding names.
diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/MacOsRomanEncoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/MacOsRomanEncoding.cs
index 3bbd06ecf..afa0e010a 100644
--- a/src/UglyToad.PdfPig.Fonts/Encodings/MacOsRomanEncoding.cs
+++ b/src/UglyToad.PdfPig.Fonts/Encodings/MacOsRomanEncoding.cs
@@ -1,12 +1,10 @@
namespace UglyToad.PdfPig.Fonts.Encodings
{
- using Core;
-
///
///
/// Similar to the with 15 additional entries.
///
- public class MacOsRomanEncoding : MacRomanEncoding
+ public sealed class MacOsRomanEncoding : MacRomanEncoding
{
private static readonly (int, string)[] EncodingTable =
{
diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/MacRomanEncoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/MacRomanEncoding.cs
index 68db2ef12..c2d18829a 100644
--- a/src/UglyToad.PdfPig.Fonts/Encodings/MacRomanEncoding.cs
+++ b/src/UglyToad.PdfPig.Fonts/Encodings/MacRomanEncoding.cs
@@ -1,8 +1,5 @@
namespace UglyToad.PdfPig.Fonts.Encodings
-{
- using Core;
- using System.Diagnostics;
-
+{
///
/// The Mac Roman encoding.
///
diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/StandardEncoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/StandardEncoding.cs
index 0ad159f7e..562dbe82f 100644
--- a/src/UglyToad.PdfPig.Fonts/Encodings/StandardEncoding.cs
+++ b/src/UglyToad.PdfPig.Fonts/Encodings/StandardEncoding.cs
@@ -3,7 +3,7 @@
///
/// The standard PDF encoding.
///
- public class StandardEncoding : Encoding
+ public sealed class StandardEncoding : Encoding
{
private static readonly (int, string)[] EncodingTable =
{
diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/SymbolEncoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/SymbolEncoding.cs
index c2d3fe92d..369ab8339 100644
--- a/src/UglyToad.PdfPig.Fonts/Encodings/SymbolEncoding.cs
+++ b/src/UglyToad.PdfPig.Fonts/Encodings/SymbolEncoding.cs
@@ -3,7 +3,7 @@
///
/// Symbol encoding.
///
- public class SymbolEncoding : Encoding
+ public sealed class SymbolEncoding : Encoding
{
///
/// EncodingTable for Symbol
diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/WinAnsiEncoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/WinAnsiEncoding.cs
index 6c12e8774..28f18fc12 100644
--- a/src/UglyToad.PdfPig.Fonts/Encodings/WinAnsiEncoding.cs
+++ b/src/UglyToad.PdfPig.Fonts/Encodings/WinAnsiEncoding.cs
@@ -3,7 +3,7 @@
///
/// Windows ANSI encoding.
///
- public class WinAnsiEncoding : Encoding
+ public sealed class WinAnsiEncoding : Encoding
{
///
/// The encoding table is taken from the Appendix of the specification.
diff --git a/src/UglyToad.PdfPig.Fonts/Encodings/ZapfDingbatsEncoding.cs b/src/UglyToad.PdfPig.Fonts/Encodings/ZapfDingbatsEncoding.cs
index f5b885f50..29a381167 100644
--- a/src/UglyToad.PdfPig.Fonts/Encodings/ZapfDingbatsEncoding.cs
+++ b/src/UglyToad.PdfPig.Fonts/Encodings/ZapfDingbatsEncoding.cs
@@ -3,7 +3,7 @@
///
/// Zapf Dingbats encoding.
///
- public class ZapfDingbatsEncoding : Encoding
+ public sealed class ZapfDingbatsEncoding : Encoding
{
///
/// EncodingTable for ZapfDingbats
diff --git a/src/UglyToad.PdfPig.Fonts/GlyphList.cs b/src/UglyToad.PdfPig.Fonts/GlyphList.cs
index 2931f1116..f17881ada 100644
--- a/src/UglyToad.PdfPig.Fonts/GlyphList.cs
+++ b/src/UglyToad.PdfPig.Fonts/GlyphList.cs
@@ -5,6 +5,7 @@
using System.Globalization;
using System.Text;
using Encodings;
+ using UglyToad.PdfPig.Util;
///
/// A list which maps PostScript glyph names to unicode values.
@@ -117,7 +118,7 @@ public string NameToUnicode(string name)
var foundUnicode = true;
for (int chPos = 3; chPos + 4 <= nameLength; chPos += 4)
{
- if (!int.TryParse(name.Substring(chPos, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var codePoint))
+ if (!int.TryParse(name.AsSpanOrSubstring(chPos, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var codePoint))
{
foundUnicode = false;
break;
@@ -138,10 +139,10 @@ public string NameToUnicode(string name)
unicode = uniStr.ToString();
}
- else if (name.StartsWith("u") && name.Length == 5)
+ else if (name.StartsWith("u", StringComparison.Ordinal) && name.Length == 5)
{
// test for an alternate Unicode name representation uXXXX
- var codePoint = int.Parse(name.Substring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ var codePoint = int.Parse(name.AsSpanOrSubstring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
if (codePoint > 0xD7FF && codePoint < 0xE000)
{
diff --git a/src/UglyToad.PdfPig.Fonts/GlyphListFactory.cs b/src/UglyToad.PdfPig.Fonts/GlyphListFactory.cs
index a5e669827..39a6cedfa 100644
--- a/src/UglyToad.PdfPig.Fonts/GlyphListFactory.cs
+++ b/src/UglyToad.PdfPig.Fonts/GlyphListFactory.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+ using Util;
internal class GlyphListFactory
{
@@ -34,6 +35,8 @@ public static GlyphList Read(Stream stream)
return ReadInternal(stream);
}
+ private static readonly char[] Semicolon = [';'];
+
private static GlyphList ReadInternal(Stream stream, int? defaultDictionaryCapacity = 0)
{
if (stream == null)
@@ -41,8 +44,8 @@ private static GlyphList ReadInternal(Stream stream, int? defaultDictionaryCapac
throw new ArgumentNullException(nameof(stream));
}
- var result = defaultDictionaryCapacity.HasValue ? new Dictionary(defaultDictionaryCapacity.Value)
- : new Dictionary();
+ var result = defaultDictionaryCapacity.HasValue ? new Dictionary(defaultDictionaryCapacity.Value) : [];
+
using (var reader = new StreamReader(stream))
{
@@ -60,7 +63,7 @@ private static GlyphList ReadInternal(Stream stream, int? defaultDictionaryCapac
continue;
}
- var parts = line.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+ var parts = line.Split(Semicolon, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
{
@@ -70,13 +73,16 @@ private static GlyphList ReadInternal(Stream stream, int? defaultDictionaryCapac
var key = parts[0];
- var values = parts[1].Split(' ');
-
+ var valueReader = new StringSplitter(parts[1].AsSpan(), ' ');
var value = string.Empty;
- foreach (var s in values)
+
+ while (valueReader.TryRead(out var s))
{
+#if NET6_0_OR_GREATER
var code = int.Parse(s, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
-
+#else
+ var code = int.Parse(s.ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+#endif
value += char.ConvertFromUtf32(code);
}
diff --git a/src/UglyToad.PdfPig.Fonts/TrueType/Glyphs/Glyph.cs b/src/UglyToad.PdfPig.Fonts/TrueType/Glyphs/Glyph.cs
index 632bd57f0..42f384d23 100644
--- a/src/UglyToad.PdfPig.Fonts/TrueType/Glyphs/Glyph.cs
+++ b/src/UglyToad.PdfPig.Fonts/TrueType/Glyphs/Glyph.cs
@@ -39,7 +39,7 @@ public Glyph(bool isSimple, byte[] instructions, ushort[] endPointsOfContours, G
public static IGlyphDescription Empty(PdfRectangle bounds)
{
- return new Glyph(true, new byte[0], new ushort[0], new GlyphPoint[0], bounds);
+ return new Glyph(true, [], [], [], bounds);
}
public IGlyphDescription DeepClone()
diff --git a/src/UglyToad.PdfPig.Fonts/TrueType/Parser/CMapTableParser.cs b/src/UglyToad.PdfPig.Fonts/TrueType/Parser/CMapTableParser.cs
index 0d6e64af2..de5e7a5b7 100644
--- a/src/UglyToad.PdfPig.Fonts/TrueType/Parser/CMapTableParser.cs
+++ b/src/UglyToad.PdfPig.Fonts/TrueType/Parser/CMapTableParser.cs
@@ -87,7 +87,7 @@ public CMapTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, Table
return new CMapTable(tableVersionNumber, header, tables);
}
- private class SubTableHeaderEntry
+ private readonly struct SubTableHeaderEntry
{
public TrueTypeCMapPlatform PlatformId { get; }
diff --git a/src/UglyToad.PdfPig.Fonts/TrueType/Subsetting/TrueTypeGlyphTableSubsetter.cs b/src/UglyToad.PdfPig.Fonts/TrueType/Subsetting/TrueTypeGlyphTableSubsetter.cs
index d74df2dd5..d508543c0 100644
--- a/src/UglyToad.PdfPig.Fonts/TrueType/Subsetting/TrueTypeGlyphTableSubsetter.cs
+++ b/src/UglyToad.PdfPig.Fonts/TrueType/Subsetting/TrueTypeGlyphTableSubsetter.cs
@@ -65,7 +65,7 @@ public static TrueTypeSubsetGlyphTable SubsetGlyphTable(TrueTypeFont font, byte[
var compositeIndicesToReplace = new List<(uint offset, ushort newIndex)>();
- using (var stream = new MemoryStream())
+ using (var writer = new ArrayPoolBufferWriter())
{
for (var i = 0; i < glyphsToCopy.Count; i++)
{
@@ -102,7 +102,7 @@ public static TrueTypeSubsetGlyphTable SubsetGlyphTable(TrueTypeFont font, byte[
}
// Record the glyph location.
- glyphLocations.Add((uint)stream.Position);
+ glyphLocations.Add((uint)writer.WrittenCount);
var advanceWidth = advanceWidthTable.HorizontalMetrics[glyphsToCopyOriginalIndex[i]];
advanceWidths.Add(advanceWidth);
@@ -123,19 +123,19 @@ public static TrueTypeSubsetGlyphTable SubsetGlyphTable(TrueTypeFont font, byte[
glyphBytes[offset] = (byte)(newIndex >> 8);
glyphBytes[offset + 1] = (byte)newIndex;
}
-
- stream.Write(glyphBytes, 0, glyphBytes.Length);
+
+ writer.Write(glyphBytes);
// Each glyph description must start at a 4 byte boundary.
var remainder = glyphBytes.Length % 4;
var bytesToPad = remainder == 0 ? 0 : 4 - remainder;
for (var j = 0; j < bytesToPad; j++)
{
- stream.WriteByte(0);
+ writer.Write(0);
}
}
- var output = stream.ToArray();
+ var output = writer.WrittenSpan.ToArray();
glyphLocations.Add((uint)output.Length);
var offsets = glyphLocations.ToArray();
diff --git a/src/UglyToad.PdfPig.Fonts/Type1/Parser/Type1Tokenizer.cs b/src/UglyToad.PdfPig.Fonts/Type1/Parser/Type1Tokenizer.cs
index 452fa46a2..333f008ed 100644
--- a/src/UglyToad.PdfPig.Fonts/Type1/Parser/Type1Tokenizer.cs
+++ b/src/UglyToad.PdfPig.Fonts/Type1/Parser/Type1Tokenizer.cs
@@ -169,7 +169,7 @@ char GetNext()
switch (c1)
{
case 'n':
- case 'r': stringBuffer.Append("\n"); break;
+ case 'r': stringBuffer.Append('\n'); break;
case 't': stringBuffer.Append('\t'); break;
case 'b': stringBuffer.Append('\b'); break;
case 'f': stringBuffer.Append('\f'); break;
@@ -180,14 +180,14 @@ char GetNext()
// octal \ddd
if (char.IsDigit(c1))
{
- var rawOctal = new string(new[] { c1, GetNext(), GetNext() });
+ var rawOctal = new string([c1, GetNext(), GetNext()]);
var code = Convert.ToInt32(rawOctal, 8);
stringBuffer.Append((char)code);
}
break;
case '\r':
case '\n':
- stringBuffer.Append("\n");
+ stringBuffer.Append('\n');
break;
default:
stringBuffer.Append(c);
diff --git a/src/UglyToad.PdfPig.Fonts/Util/StringExtensions.cs b/src/UglyToad.PdfPig.Fonts/Util/StringExtensions.cs
new file mode 100644
index 000000000..38d4ec5df
--- /dev/null
+++ b/src/UglyToad.PdfPig.Fonts/Util/StringExtensions.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace UglyToad.PdfPig.Util;
+
+internal static class StringExtensions
+{
+#if NET
+ public static ReadOnlySpan AsSpanOrSubstring(this string text, int start)
+ {
+ return text.AsSpan(start);
+ }
+
+ public static ReadOnlySpan AsSpanOrSubstring(this string text, int start, int length)
+ {
+ return text.AsSpan(start, length);
+ }
+#else
+ public static string AsSpanOrSubstring(this string text, int start)
+ {
+ return text.Substring(start);
+ }
+
+ public static string AsSpanOrSubstring(this string text, int start, int length)
+ {
+ return text.Substring(start, length);
+ }
+#endif
+}
diff --git a/src/UglyToad.PdfPig.Fonts/Util/StringSplitter.cs b/src/UglyToad.PdfPig.Fonts/Util/StringSplitter.cs
new file mode 100644
index 000000000..e86453d10
--- /dev/null
+++ b/src/UglyToad.PdfPig.Fonts/Util/StringSplitter.cs
@@ -0,0 +1,70 @@
+using System;
+using System.IO;
+
+namespace UglyToad.PdfPig.Util;
+
+internal ref struct StringSplitter(ReadOnlySpan text, char separator)
+{
+ private readonly ReadOnlySpan text = text;
+ private readonly char separator = separator;
+ private int position = 0;
+
+ public bool TryRead(out ReadOnlySpan result)
+ {
+ if (IsEof)
+ {
+ result = default;
+
+ return false;
+ }
+
+ int separatorIndex = text.Slice(position).IndexOf(separator);
+
+ if (separatorIndex > -1)
+ {
+ result = text.Slice(position, separatorIndex);
+
+ position += separatorIndex + 1;
+ }
+ else
+ {
+ result = text.Slice(position);
+
+ position = text.Length;
+ }
+
+ return true;
+ }
+
+ public ReadOnlySpan Read()
+ {
+ if (IsEof)
+ {
+ ThrowEof();
+ }
+
+ int start = position;
+
+ int separatorIndex = text.Slice(position).IndexOf(separator);
+
+ if (separatorIndex > -1)
+ {
+ position += separatorIndex + 1;
+
+ return text.Slice(start, separatorIndex);
+ }
+ else
+ {
+ position = text.Length;
+
+ return text.Slice(start);
+ }
+ }
+
+ public readonly bool IsEof => position == text.Length;
+
+ private static void ThrowEof()
+ {
+ throw new EndOfStreamException();
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698363-0.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698363-0.pdf
new file mode 100644
index 000000000..15cea29c1
Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/GHOSTSCRIPT-698363-0.pdf differ
diff --git a/src/UglyToad.PdfPig.Tests/Integration/EncodingsTests.cs b/src/UglyToad.PdfPig.Tests/Integration/EncodingsTests.cs
index deeda366f..a8dd79a31 100644
--- a/src/UglyToad.PdfPig.Tests/Integration/EncodingsTests.cs
+++ b/src/UglyToad.PdfPig.Tests/Integration/EncodingsTests.cs
@@ -5,6 +5,22 @@
public class EncodingsTests
{
+ [Fact]
+ public void Windows1252Encoding()
+ {
+ string path = IntegrationHelpers.GetDocumentPath("GHOSTSCRIPT-698363-0.pdf");
+ using (var document = PdfDocument.Open(path))
+ {
+ var page = document.GetPage(1);
+ string actual = string.Concat(page.Letters.Select(l => l.Value));
+
+ // The expected string value is just here to make sure we have the same results across net versions.
+ // Feel free to correct/update it if chars are not actually correct.
+ string expected = "ҘҹЧѥЧКጹѝঐܮ̂ҥ҇ҁӃ࿋\u0c0dҀғҊ˺෨ཌආр෨ཌ̂ҘҹЧѥЧКጹѝঐܮ̂ҥ҇ҁӃ࿋\u0c0dҀғҊ˺෨ཌආр෨ཌ̂ݰႺംࢥ༢࣭\u089aѽ̔ҫһҐ̔ݰႺംࢥ༢࣭\u089aѽ̔ҫһҐ̔";
+ Assert.Equal(expected, actual);
+ }
+ }
+
[Fact]
public void Issue688()
{
diff --git a/src/UglyToad.PdfPig.Tests/Integration/IntegrationDocumentTests.cs b/src/UglyToad.PdfPig.Tests/Integration/IntegrationDocumentTests.cs
index 842dfa7d1..8b628fc6f 100644
--- a/src/UglyToad.PdfPig.Tests/Integration/IntegrationDocumentTests.cs
+++ b/src/UglyToad.PdfPig.Tests/Integration/IntegrationDocumentTests.cs
@@ -3,10 +3,11 @@
public class IntegrationDocumentTests
{
private static readonly Lazy DocumentFolder = new Lazy(() => Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "Integration", "Documents")));
- private static readonly HashSet _documentsToIgnore = new HashSet()
- {
- "issue_671.pdf"
- };
+ private static readonly HashSet _documentsToIgnore =
+ [
+ "issue_671.pdf",
+ "GHOSTSCRIPT-698363-0.pdf"
+ ];
[Theory]
[MemberData(nameof(GetAllDocuments))]
diff --git a/src/UglyToad.PdfPig.Tests/Integration/StreamProcessorTests.cs b/src/UglyToad.PdfPig.Tests/Integration/StreamProcessorTests.cs
index 16f5a49cc..9998eea13 100644
--- a/src/UglyToad.PdfPig.Tests/Integration/StreamProcessorTests.cs
+++ b/src/UglyToad.PdfPig.Tests/Integration/StreamProcessorTests.cs
@@ -148,9 +148,9 @@ public override void RenderGlyph(IFont font,
int code,
string unicode,
long currentOffset,
- TransformationMatrix renderingMatrix,
- TransformationMatrix textMatrix,
- TransformationMatrix transformationMatrix,
+ in TransformationMatrix renderingMatrix,
+ in TransformationMatrix textMatrix,
+ in TransformationMatrix transformationMatrix,
CharacterBoundingBox characterBoundingBox)
{
_letters.Add(unicode);
diff --git a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaGlyphStreamProcessor.cs b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaGlyphStreamProcessor.cs
index 3253cb1f3..d0d82dc52 100644
--- a/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaGlyphStreamProcessor.cs
+++ b/src/UglyToad.PdfPig.Tests/Integration/VisualVerification/SkiaHelpers/SkiaGlyphStreamProcessor.cs
@@ -69,9 +69,9 @@ public override void RenderGlyph(IFont font,
int code,
string unicode,
long currentOffset,
- TransformationMatrix renderingMatrix,
- TransformationMatrix textMatrix,
- TransformationMatrix transformationMatrix,
+ in TransformationMatrix renderingMatrix,
+ in TransformationMatrix textMatrix,
+ in TransformationMatrix transformationMatrix,
CharacterBoundingBox characterBoundingBox)
{
if (textRenderingMode == TextRenderingMode.Neither)
@@ -98,9 +98,9 @@ private void ShowVectorFontGlyph(SKPath path,
IColor strokingColor,
IColor nonStrokingColor,
TextRenderingMode textRenderingMode,
- TransformationMatrix renderingMatrix,
- TransformationMatrix textMatrix,
- TransformationMatrix transformationMatrix)
+ in TransformationMatrix renderingMatrix,
+ in TransformationMatrix textMatrix,
+ in TransformationMatrix transformationMatrix)
{
var transformMatrix = renderingMatrix.ToSKMatrix()
.PostConcat(textMatrix.ToSKMatrix())
diff --git a/src/UglyToad.PdfPig.Tokenization/NameTokenizer.cs b/src/UglyToad.PdfPig.Tokenization/NameTokenizer.cs
index 03cf97b7b..a2d6cfbd2 100644
--- a/src/UglyToad.PdfPig.Tokenization/NameTokenizer.cs
+++ b/src/UglyToad.PdfPig.Tokenization/NameTokenizer.cs
@@ -5,9 +5,18 @@
using Core;
using Tokens;
+#if NET8_0_OR_GREATER
+ using System.Text.Unicode;
+#endif
+
internal class NameTokenizer : ITokenizer
{
- private static readonly ListPool ListPool = new ListPool(10);
+ static NameTokenizer()
+ {
+#if NET6_0_OR_GREATER
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+#endif
+ }
public bool ReadsNextByte { get; } = true;
@@ -20,11 +29,11 @@ public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken tok
return false;
}
- var bytes = ListPool.Borrow();
+ using var bytes = new ArrayPoolBufferWriter();
bool escapeActive = false;
int postEscapeRead = 0;
- var escapedChars = new char[2];
+ Span escapedChars = stackalloc char[2];
while (inputBytes.MoveNext())
{
@@ -43,10 +52,12 @@ public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken tok
if (postEscapeRead == 2)
{
- var hex = new string(escapedChars);
+ int high = escapedChars[0] <= '9' ? escapedChars[0] - '0' : char.ToUpper(escapedChars[0]) - 'A' + 10;
+ int low = escapedChars[1] <= '9' ? escapedChars[1] - '0' : char.ToUpper(escapedChars[1]) - 'A' + 10;
- var characterToWrite = (byte)Convert.ToInt32(hex, 16);
- bytes.Add(characterToWrite);
+ byte characterToWrite = (byte)(high * 16 + low);
+
+ bytes.Write(characterToWrite);
escapeActive = false;
postEscapeRead = 0;
@@ -54,11 +65,11 @@ public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken tok
}
else
{
- bytes.Add((byte)'#');
+ bytes.Write((byte)'#');
if (postEscapeRead == 1)
{
- bytes.Add((byte)escapedChars[0]);
+ bytes.Write((byte)escapedChars[0]);
}
if (ReadHelper.IsEndOfName(b))
@@ -75,7 +86,7 @@ public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken tok
continue;
}
- bytes.Add(b);
+ bytes.Write(b);
escapeActive = false;
postEscapeRead = 0;
}
@@ -87,18 +98,22 @@ public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken tok
}
else
{
- bytes.Add(b);
+ bytes.Write(b);
}
}
- var byteArray = bytes.ToArray();
-
- ListPool.Return(bytes);
+#if NET8_0_OR_GREATER
+ var byteArray = bytes.WrittenSpan;
+ bool isValidUtf8 = Utf8.IsValid(byteArray);
+#else
+ var byteArray = bytes.WrittenSpan.ToArray();
+ bool isValidUtf8 = ReadHelper.IsValidUtf8(byteArray);
+#endif
- var str = ReadHelper.IsValidUtf8(byteArray)
+ var str = isValidUtf8
? Encoding.UTF8.GetString(byteArray)
: Encoding.GetEncoding("windows-1252").GetString(byteArray);
-
+
token = NameToken.Create(str);
return true;
diff --git a/src/UglyToad.PdfPig/Content/BasePageFactory.cs b/src/UglyToad.PdfPig/Content/BasePageFactory.cs
index be751220a..c80cb32ab 100644
--- a/src/UglyToad.PdfPig/Content/BasePageFactory.cs
+++ b/src/UglyToad.PdfPig/Content/BasePageFactory.cs
@@ -176,7 +176,7 @@ private TPage ProcessPageInternal(
CropBox cropBox,
UserSpaceUnit userSpaceUnit,
PageRotationDegrees rotation,
- TransformationMatrix initialMatrix,
+ in TransformationMatrix initialMatrix,
ReadOnlyMemory contentBytes)
{
IReadOnlyList operations;
@@ -315,7 +315,7 @@ protected MediaBox GetMediaBox(int number, DictionaryToken dictionary, PageTreeM
///
///
///
- protected static void ApplyTransformNormalise(TransformationMatrix transformationMatrix, ref MediaBox mediaBox, ref CropBox cropBox)
+ protected static void ApplyTransformNormalise(in TransformationMatrix transformationMatrix, ref MediaBox mediaBox, ref CropBox cropBox)
{
if (transformationMatrix != TransformationMatrix.Identity)
{
diff --git a/src/UglyToad.PdfPig/Encryption/AesEncryptionHelper.cs b/src/UglyToad.PdfPig/Encryption/AesEncryptionHelper.cs
index a6be20ae6..44c7fcb1a 100644
--- a/src/UglyToad.PdfPig/Encryption/AesEncryptionHelper.cs
+++ b/src/UglyToad.PdfPig/Encryption/AesEncryptionHelper.cs
@@ -26,6 +26,9 @@ public static byte[] Decrypt(byte[] data, byte[] finalKey)
aes.Key = finalKey;
aes.IV = iv;
+#if NET8_0_OR_GREATER
+ return aes.DecryptCbc(data.AsSpan(iv.Length), iv, PaddingMode.PKCS7);
+#else
var buffer = new byte[256];
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
@@ -49,6 +52,7 @@ public static byte[] Decrypt(byte[] data, byte[] finalKey)
return output.ToArray();
}
}
+#endif
}
}
}
diff --git a/src/UglyToad.PdfPig/Graphics/BaseStreamProcessor.cs b/src/UglyToad.PdfPig/Graphics/BaseStreamProcessor.cs
index a6a20c4b8..45c66218f 100644
--- a/src/UglyToad.PdfPig/Graphics/BaseStreamProcessor.cs
+++ b/src/UglyToad.PdfPig/Graphics/BaseStreamProcessor.cs
@@ -120,7 +120,7 @@ protected BaseStreamProcessor(
CropBox cropBox,
UserSpaceUnit userSpaceUnit,
PageRotationDegrees rotation,
- TransformationMatrix initialMatrix,
+ in TransformationMatrix initialMatrix,
ParsingOptions parsingOptions)
{
this.PageNumber = pageNumber;
@@ -325,9 +325,9 @@ public abstract void RenderGlyph(IFont font,
int code,
string unicode,
long currentOffset,
- TransformationMatrix renderingMatrix,
- TransformationMatrix textMatrix,
- TransformationMatrix transformationMatrix,
+ in TransformationMatrix renderingMatrix,
+ in TransformationMatrix textMatrix,
+ in TransformationMatrix transformationMatrix,
CharacterBoundingBox characterBoundingBox);
///
diff --git a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
index 40d50db7e..db4b05ac1 100644
--- a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
+++ b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
@@ -95,9 +95,9 @@ public override void RenderGlyph(IFont font,
int code,
string unicode,
long currentOffset,
- TransformationMatrix renderingMatrix,
- TransformationMatrix textMatrix,
- TransformationMatrix transformationMatrix,
+ in TransformationMatrix renderingMatrix,
+ in TransformationMatrix textMatrix,
+ in TransformationMatrix transformationMatrix,
CharacterBoundingBox characterBoundingBox)
{
var transformedGlyphBounds = PerformantRectangleTransformer
diff --git a/src/UglyToad.PdfPig/Graphics/InlineImageBuilder.cs b/src/UglyToad.PdfPig/Graphics/InlineImageBuilder.cs
index 4d7d3b679..1f89ac523 100644
--- a/src/UglyToad.PdfPig/Graphics/InlineImageBuilder.cs
+++ b/src/UglyToad.PdfPig/Graphics/InlineImageBuilder.cs
@@ -25,7 +25,8 @@ public sealed class InlineImageBuilder
///
public ReadOnlyMemory Bytes { get; internal set; }
- internal InlineImage CreateInlineImage(TransformationMatrix transformationMatrix,
+ internal InlineImage CreateInlineImage(
+ in TransformationMatrix transformationMatrix,
ILookupFilterProvider filterProvider,
IPdfTokenScanner tokenScanner,
RenderingIntent defaultRenderingIntent,
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/OperationWriteHelper.cs b/src/UglyToad.PdfPig/Graphics/Operations/OperationWriteHelper.cs
index 2b8ce762e..d2952ff6b 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/OperationWriteHelper.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/OperationWriteHelper.cs
@@ -1,10 +1,10 @@
namespace UglyToad.PdfPig.Graphics.Operations
{
- using PdfPig.Core;
using System;
using System.Globalization;
using System.IO;
- using UglyToad.PdfPig.Util;
+ using PdfPig.Core;
+ using Util;
internal static class OperationWriteHelper
{
@@ -23,9 +23,15 @@ public static void WriteText(this Stream stream, string text, bool appendWhitesp
public static void WriteHex(this Stream stream, ReadOnlySpan bytes)
{
- var text = Hex.GetString(bytes);
+ Span hex = bytes.Length <= 64
+ ? stackalloc byte[bytes.Length * 2]
+ : new byte[bytes.Length * 2];
+
+ Hex.GetUtf8Chars(bytes, hex);
- stream.WriteText($"<{text}>");
+ stream.WriteByte((byte)'<');
+ stream.Write(hex);
+ stream.WriteByte((byte)'>');
}
public static void WriteWhiteSpace(this Stream stream)
diff --git a/src/UglyToad.PdfPig/Graphics/PerformantRectangleTransformer.cs b/src/UglyToad.PdfPig/Graphics/PerformantRectangleTransformer.cs
index 1e2ac4b02..5097640b2 100644
--- a/src/UglyToad.PdfPig/Graphics/PerformantRectangleTransformer.cs
+++ b/src/UglyToad.PdfPig/Graphics/PerformantRectangleTransformer.cs
@@ -10,7 +10,7 @@ public static class PerformantRectangleTransformer
///
/// Transform the rectangle using the matrices.
///
- public static PdfRectangle Transform(TransformationMatrix first, TransformationMatrix second, TransformationMatrix third, PdfRectangle rectangle)
+ public static PdfRectangle Transform(in TransformationMatrix first, in TransformationMatrix second, in TransformationMatrix third, PdfRectangle rectangle)
{
var tl = rectangle.TopLeft;
var tr = rectangle.TopRight;
diff --git a/src/UglyToad.PdfPig/Util/Hex.cs b/src/UglyToad.PdfPig/Util/Hex.cs
index 240d0af98..3a01904e1 100644
--- a/src/UglyToad.PdfPig/Util/Hex.cs
+++ b/src/UglyToad.PdfPig/Util/Hex.cs
@@ -1,7 +1,6 @@
namespace UglyToad.PdfPig.Util
{
using System;
- using System.Text;
/**
* Utility functions for hex encoding.
@@ -17,7 +16,18 @@ internal static class Hex
*
*/
private static readonly char[] HexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
-
+
+ public static void GetUtf8Chars(ReadOnlySpan bytes, Span utf8Chars)
+ {
+ int position = 0;
+
+ foreach (var b in bytes)
+ {
+ utf8Chars[position++] = (byte)HexChars[GetHighNibble(b)];
+ utf8Chars[position++] = (byte)HexChars[GetLowNibble(b)];
+ }
+ }
+
///
/// Returns a hex string for the given byte array.
///
@@ -26,14 +36,16 @@ public static string GetString(ReadOnlySpan bytes)
#if NET6_0_OR_GREATER
return Convert.ToHexString(bytes);
#else
- var stringBuilder = new StringBuilder(bytes.Length * 2);
+ var chars = new char[bytes.Length * 2];
+ int position = 0;
foreach (var b in bytes)
{
- stringBuilder.Append(HexChars[GetHighNibble(b)]).Append(HexChars[GetLowNibble(b)]);
+ chars[position++] = HexChars[GetHighNibble(b)];
+ chars[position++] = HexChars[GetLowNibble(b)];
}
- return stringBuilder.ToString();
+ return new string(chars);
#endif
}
diff --git a/src/UglyToad.PdfPig/Util/PatternParser.cs b/src/UglyToad.PdfPig/Util/PatternParser.cs
index d1aee3c7c..0988cd08a 100644
--- a/src/UglyToad.PdfPig/Util/PatternParser.cs
+++ b/src/UglyToad.PdfPig/Util/PatternParser.cs
@@ -67,7 +67,7 @@ public static PatternColor Create(IToken pattern, IPdfTokenScanner scanner, IRes
}
private static PatternColor CreateTilingPattern(StreamToken patternStream, DictionaryToken patternExtGState,
- TransformationMatrix matrix, IPdfTokenScanner scanner)
+ in TransformationMatrix matrix, IPdfTokenScanner scanner)
{
if (!patternStream.StreamDictionary.TryGet(NameToken.PaintType, scanner, out var paintTypeToken))
{
@@ -113,7 +113,7 @@ private static PatternColor CreateTilingPattern(StreamToken patternStream, Dicti
}
private static PatternColor CreateShadingPattern(DictionaryToken patternDictionary, DictionaryToken? patternExtGState,
- TransformationMatrix matrix, IPdfTokenScanner scanner, IResourceStore resourceStore,
+ in TransformationMatrix matrix, IPdfTokenScanner scanner, IResourceStore resourceStore,
ILookupFilterProvider filterProvider)
{
IToken shadingToken = patternDictionary.Data[NameToken.Shading];
diff --git a/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs b/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs
index fbc48132d..fa99a38bf 100644
--- a/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs
+++ b/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs
@@ -989,7 +989,7 @@ public PdfPageBuilder CopyFrom(Page srcPage)
return this;
}
- private List DrawLetters(NameToken? name, string text, IWritingFont font, TransformationMatrix fontMatrix, double fontSize, TransformationMatrix textMatrix)
+ private List DrawLetters(NameToken? name, string text, IWritingFont font, in TransformationMatrix fontMatrix, double fontSize, TransformationMatrix textMatrix)
{
var horizontalScaling = 1;
var rise = 0;