2020-01-05 12:08:01 +00:00
namespace UglyToad.PdfPig.Fonts.CompactFontFormat
2018-11-16 21:30:59 +00:00
{
using System ;
2018-11-18 13:53:43 +00:00
using System.Diagnostics ;
2018-11-16 21:30:59 +00:00
using System.Text ;
/// <summary>
/// Provides access to the raw bytes of this Compact Font Format file with utility methods for reading data types from it.
/// </summary>
2020-01-05 12:08:01 +00:00
public class CompactFontFormatData
2018-11-16 21:30:59 +00:00
{
2024-04-01 16:04:54 -07:00
private readonly ReadOnlyMemory < byte > dataBytes ;
2018-11-16 21:30:59 +00:00
2020-01-05 12:08:01 +00:00
/// <summary>
/// The current position in the data.
/// </summary>
2018-11-16 21:30:59 +00:00
public int Position { get ; private set ; } = - 1 ;
2020-01-05 12:08:01 +00:00
/// <summary>
/// The length of the data.
/// </summary>
2024-04-01 16:04:54 -07:00
public int Length = > dataBytes . Length ;
2018-11-18 13:53:43 +00:00
2020-01-05 12:08:01 +00:00
/// <summary>
/// Create a new <see cref="CompactFontFormatData"/>.
/// </summary>
2018-11-18 13:53:43 +00:00
[DebuggerStepThrough]
2024-04-01 16:04:54 -07:00
public CompactFontFormatData ( ReadOnlyMemory < byte > dataBytes )
2018-11-16 21:30:59 +00:00
{
this . dataBytes = dataBytes ;
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read a string of the specified length.
/// </summary>
2018-11-16 21:30:59 +00:00
public string ReadString ( int length , Encoding encoding )
{
2024-04-18 11:58:40 -07:00
return encoding . GetString ( ReadSpan ( length ) ) ;
2018-11-16 21:30:59 +00:00
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read Card8 format.
/// </summary>
2018-11-16 21:30:59 +00:00
public byte ReadCard8 ( )
{
return ReadByte ( ) ;
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read Card16 format.
/// </summary>
2018-11-16 21:30:59 +00:00
public ushort ReadCard16 ( )
{
return ( ushort ) ( ReadByte ( ) < < 8 | ReadByte ( ) ) ;
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read Offsize.
/// </summary>
2018-11-16 21:30:59 +00:00
public byte ReadOffsize ( )
{
return ReadByte ( ) ;
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read Offset.
/// </summary>
2018-11-16 21:30:59 +00:00
public int ReadOffset ( int offsetSize )
{
var value = 0 ;
for ( var i = 0 ; i < offsetSize ; i + + )
{
value = value < < 8 | ReadByte ( ) ;
}
return value ;
}
2024-04-18 11:58:40 -07:00
internal ReadOnlySpan < byte > 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 ;
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read byte.
/// </summary>
2018-11-16 21:30:59 +00:00
public byte ReadByte ( )
{
Position + + ;
2024-04-01 16:04:54 -07:00
if ( Position > = dataBytes . Length )
2018-11-16 21:30:59 +00:00
{
2024-04-01 16:04:54 -07:00
throw new IndexOutOfRangeException ( $"Cannot read byte at position {Position} of an array which is {dataBytes.Length} bytes long." ) ;
2018-11-16 21:30:59 +00:00
}
2024-04-01 16:04:54 -07:00
return dataBytes . Span [ Position ] ;
2018-11-16 21:30:59 +00:00
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Peek the next byte without advancing the data.
/// </summary>
2018-11-16 21:30:59 +00:00
public byte Peek ( )
{
2024-04-01 16:04:54 -07:00
return dataBytes . Span [ Position + 1 ] ;
2018-11-16 21:30:59 +00:00
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Whether there's more data to read in the input.
/// </summary>
2018-11-16 21:30:59 +00:00
public bool CanRead ( )
{
2024-04-01 16:04:54 -07:00
return Position < dataBytes . Length - 1 ;
2018-11-16 21:30:59 +00:00
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Move to the given offset from the beginning.
/// </summary>
2018-11-16 21:30:59 +00:00
public void Seek ( int offset )
{
Position = offset - 1 ;
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read long.
/// </summary>
2018-11-16 21:30:59 +00:00
public long ReadLong ( )
{
return ( ReadCard16 ( ) < < 16 ) | ReadCard16 ( ) ;
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read sid.
/// </summary>
2018-11-16 23:32:18 +00:00
public int ReadSid ( )
{
return ReadByte ( ) < < 8 | ReadByte ( ) ;
}
2020-01-05 12:08:01 +00:00
/// <summary>
/// Read byte array of given length.
/// </summary>
2018-11-16 21:30:59 +00:00
public byte [ ] ReadBytes ( int length )
{
var result = new byte [ length ] ;
for ( int i = 0 ; i < length ; i + + )
{
result [ i ] = ReadByte ( ) ;
}
return result ;
}
2018-11-18 13:53:43 +00:00
2020-01-05 12:08:01 +00:00
/// <summary>
/// Create a new <see cref="CompactFontFormatData"/> from this data with a snapshot at the position and length.
/// </summary>
2018-11-18 13:53:43 +00:00
public CompactFontFormatData SnapshotPortion ( int startLocation , int length )
{
2019-05-11 10:00:04 +01:00
if ( length = = 0 )
{
2024-03-13 18:59:05 -07:00
return new CompactFontFormatData ( Array . Empty < byte > ( ) ) ;
2019-05-11 10:00:04 +01:00
}
2024-04-01 16:04:54 -07:00
if ( startLocation > dataBytes . Length - 1 | | startLocation + length > dataBytes . Length )
2018-11-18 13:53:43 +00:00
{
2024-04-01 16:04:54 -07:00
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}." ) ;
2018-11-18 13:53:43 +00:00
}
var newData = new byte [ length ] ;
2018-11-24 19:02:06 +00:00
var newI = 0 ;
for ( var i = startLocation ; i < startLocation + length ; i + + )
{
2024-04-01 16:04:54 -07:00
newData [ newI ] = dataBytes . Span [ i ] ;
2018-11-24 19:02:06 +00:00
newI + + ;
}
2018-11-18 13:53:43 +00:00
return new CompactFontFormatData ( newData ) ;
}
2018-11-16 21:30:59 +00:00
}
}