namespace UglyToad.PdfPig.Fonts.CompactFontFormat { using System; using System.Diagnostics; using System.Text; /// /// Provides access to the raw bytes of this Compact Font Format file with utility methods for reading data types from it. /// public class CompactFontFormatData { private readonly ReadOnlyMemory dataBytes; /// /// The current position in the data. /// public int Position { get; private set; } = -1; /// /// The length of the data. /// public int Length => dataBytes.Length; /// /// Create a new . /// [DebuggerStepThrough] public CompactFontFormatData(ReadOnlyMemory dataBytes) { this.dataBytes = dataBytes; } /// /// Read a string of the specified length. /// public string ReadString(int length, Encoding encoding) { return encoding.GetString(ReadSpan(length)); } /// /// Read Card8 format. /// public byte ReadCard8() { return ReadByte(); } /// /// Read Card16 format. /// public ushort ReadCard16() { return (ushort)(ReadByte() << 8 | ReadByte()); } /// /// Read Offsize. /// public byte ReadOffsize() { return ReadByte(); } /// /// Read Offset. /// public int ReadOffset(int offsetSize) { var value = 0; for (var i = 0; i < offsetSize; i++) { value = value << 8 | ReadByte(); } 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. /// public byte ReadByte() { Position++; if (Position >= dataBytes.Length) { throw new IndexOutOfRangeException($"Cannot read byte at position {Position} of an array which is {dataBytes.Length} bytes long."); } return dataBytes.Span[Position]; } /// /// Peek the next byte without advancing the data. /// public byte Peek() { return dataBytes.Span[Position + 1]; } /// /// Whether there's more data to read in the input. /// public bool CanRead() { return Position < dataBytes.Length - 1; } /// /// Move to the given offset from the beginning. /// public void Seek(int offset) { Position = offset - 1; } /// /// Read long. /// public long ReadLong() { return (ReadCard16() << 16) | ReadCard16(); } /// /// Read sid. /// public int ReadSid() { return ReadByte() << 8 | ReadByte(); } /// /// Read byte array of given length. /// public byte[] ReadBytes(int length) { var result = new byte[length]; for (int i = 0; i < length; i++) { result[i] = ReadByte(); } return result; } /// /// Create a new from this data with a snapshot at the position and length. /// public CompactFontFormatData SnapshotPortion(int startLocation, int length) { if (length == 0) { return new CompactFontFormatData(Array.Empty()); } if (startLocation > dataBytes.Length - 1 || startLocation + length > dataBytes.Length) { throw new ArgumentException($"Attempted to create a snapshot of an invalid portion of the data. Length was {dataBytes.Length}, requested start: {startLocation} and requested length: {length}."); } var newData = new byte[length]; var newI = 0; for (var i = startLocation; i < startLocation + length; i++) { newData[newI] = dataBytes.Span[i]; newI++; } return new CompactFontFormatData(newData); } } }