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;