99namespace SixLabors . Fonts ;
1010
1111/// <summary>
12+ /// <para>
1213/// A binary reader that reads in big-endian format.
14+ /// </para>
15+ /// <para>
16+ /// This reader captures the stream position at construction time as <c>startOfStream</c>.
17+ /// All offset values read from OpenType tables (via <see cref="ReadOffset16"/>,
18+ /// <see cref="ReadOffset32"/>, etc.) are raw values relative to wherever the spec says
19+ /// they originate (typically the start of the containing table).
20+ /// </para>
21+ /// <para>
22+ /// When seeking with <see cref="Seek"/> using <see cref="SeekOrigin.Begin"/>, the
23+ /// <c>startOfStream</c> is automatically added to the supplied offset. This means
24+ /// table-relative offsets can be passed directly to <see cref="Seek"/> without manually
25+ /// adding the table's absolute position. Do <strong>not</strong> add the table start
26+ /// yourself — that would double-count and land at the wrong position.
27+ /// </para>
28+ /// <para>
29+ /// In contrast, <see cref="BaseStream"/>.<see cref="Stream.Position"/> always returns the
30+ /// <strong>absolute</strong> position within the underlying stream and is unaffected by
31+ /// <c>startOfStream</c>.
32+ /// </para>
1333/// </summary>
1434[ DebuggerDisplay ( "Start: {StartOfStream}, Position: {BaseStream.Position}" ) ]
1535internal sealed class BigEndianBinaryReader : IDisposable
1636{
1737 /// <summary>
18- /// Buffer used for temporary storage before conversion into primitives
38+ /// Buffer used for temporary storage before conversion into primitives.
1939 /// </summary>
2040 private readonly byte [ ] buffer = new byte [ 16 ] ;
21-
22- private readonly long startOfStream ;
2341 private readonly bool leaveOpen ;
2442
2543 /// <summary>
2644 /// Initializes a new instance of the <see cref="BigEndianBinaryReader" /> class.
27- /// Constructs a new binary reader with the given bit converter, reading
28- /// to the given stream, using the given encoding.
45+ /// The current position of <paramref name="stream"/> is captured as <c>startOfStream</c>
46+ /// and used as the origin for all subsequent <see cref="Seek"/> calls with
47+ /// <see cref="SeekOrigin.Begin"/>.
2948 /// </summary>
30- /// <param name="stream">Stream to read data from</param>
31- /// <param name="leaveOpen">if set to <c> true</c> [leave open] .</param>
49+ /// <param name="stream">Stream to read data from. </param>
50+ /// <param name="leaveOpen">If <see langword=" true"/>, the stream is not disposed when this reader is disposed .</param>
3251 public BigEndianBinaryReader ( Stream stream , bool leaveOpen )
3352 {
3453 this . BaseStream = stream ;
35- this . startOfStream = stream . Position ;
54+ this . StartOfStream = stream . Position ;
3655 this . leaveOpen = leaveOpen ;
3756 }
3857
3958 /// <summary>
4059 /// Gets the underlying stream of the EndianBinaryReader.
60+ /// Note that <see cref="Stream.Position"/> on this stream is always the
61+ /// <strong>absolute</strong> position and is <strong>not</strong> adjusted by
62+ /// <c>startOfStream</c>. Avoid using <c>BaseStream.Position</c> to compute
63+ /// offsets for <see cref="Seek"/> — use raw offsets from <see cref="ReadOffset16"/>,
64+ /// <see cref="ReadOffset32"/>, etc. instead.
4165 /// </summary>
4266 public Stream BaseStream { get ; }
4367
68+ /// <summary>
69+ /// Gets the absolute stream position captured at construction time.
70+ /// This is the origin for all <see cref="SeekOrigin.Begin"/> seeks.
71+ /// </summary>
72+ public long StartOfStream { get ; }
73+
4474 /// <summary>
4575 /// Seeks within the stream.
76+ /// When <paramref name="origin"/> is <see cref="SeekOrigin.Begin"/>, <c>startOfStream</c>
77+ /// is automatically added to <paramref name="offset"/>, so callers should pass
78+ /// table-relative offsets directly (e.g. values read from <see cref="ReadOffset16"/>
79+ /// or <see cref="ReadOffset32"/>). Do <strong>not</strong> add the table's absolute
80+ /// position — that would double-count.
4681 /// </summary>
47- /// <param name="offset">Offset to seek to.</param>
48- /// <param name="origin">Origin of seek operation. If SeekOrigin.Begin, the offset will be set to the start of stream position. </param>
82+ /// <param name="offset">Offset to seek to, relative to <paramref name="origin"/> .</param>
83+ /// <param name="origin">Origin of seek operation.</param>
4984 public void Seek ( long offset , SeekOrigin origin )
5085 {
51- // If SeekOrigin.Begin, the offset will be set to the start of stream position.
5286 if ( origin == SeekOrigin . Begin )
5387 {
54- offset += this . startOfStream ;
88+ offset += this . StartOfStream ;
5589 }
5690
57- this . BaseStream . Seek ( offset , origin ) ;
91+ _ = this . BaseStream . Seek ( offset , origin ) ;
5892 }
5993
6094 /// <summary>
@@ -67,10 +101,15 @@ public byte ReadByte()
67101 return this . buffer [ 0 ] ;
68102 }
69103
104+ /// <summary>
105+ /// Reads a single byte from the stream and reinterprets it as the specified enum type.
106+ /// </summary>
107+ /// <typeparam name="TEnum">The enum type whose underlying type must be a single byte.</typeparam>
108+ /// <returns>The enum value.</returns>
70109 public TEnum ReadByte < TEnum > ( )
71110 where TEnum : struct , Enum
72111 {
73- TryConvert ( this . ReadByte ( ) , out TEnum value ) ;
112+ _ = TryConvert ( this . ReadByte ( ) , out TEnum value ) ;
74113 return value ;
75114 }
76115
@@ -84,6 +123,11 @@ public sbyte ReadSByte()
84123 return unchecked ( ( sbyte ) this . buffer [ 0 ] ) ;
85124 }
86125
126+ /// <summary>
127+ /// Reads a 2.14 fixed-point number from the stream.
128+ /// 2 bytes are read and divided by 16384 to produce a value in the range [-2, +2).
129+ /// </summary>
130+ /// <returns>The fixed-point value as a <see cref="float"/>.</returns>
87131 public float ReadF2Dot14 ( )
88132 {
89133 const float f2Dot14ToFloat = 16384F ;
@@ -102,10 +146,15 @@ public short ReadInt16()
102146 return BinaryPrimitives . ReadInt16BigEndian ( this . buffer ) ;
103147 }
104148
149+ /// <summary>
150+ /// Reads a 16-bit integer from the stream and reinterprets it as the specified enum type.
151+ /// </summary>
152+ /// <typeparam name="TEnum">The enum type whose underlying type must be 16 bits.</typeparam>
153+ /// <returns>The enum value.</returns>
105154 public TEnum ReadInt16 < TEnum > ( )
106155 where TEnum : struct , Enum
107156 {
108- TryConvert ( this . ReadUInt16 ( ) , out TEnum value ) ;
157+ _ = TryConvert ( this . ReadUInt16 ( ) , out TEnum value ) ;
109158 return value ;
110159 }
111160
@@ -115,6 +164,11 @@ public TEnum ReadInt16<TEnum>()
115164 /// <returns>A 16-bit signed integer read from the stream, interpreted as an FWORD value.</returns>
116165 public short ReadFWORD ( ) => this . ReadInt16 ( ) ;
117166
167+ /// <summary>
168+ /// Reads an array of FWORD (signed 16-bit) values from the stream.
169+ /// </summary>
170+ /// <param name="length">The number of values to read.</param>
171+ /// <returns>An array of 16-bit signed integers.</returns>
118172 public short [ ] ReadFWORDArray ( int length ) => this . ReadInt16Array ( length ) ;
119173
120174 /// <summary>
@@ -171,25 +225,31 @@ public ushort ReadUInt16()
171225
172226 /// <summary>
173227 /// Reads a 16-bit unsigned integer from the stream representing an offset position.
174- /// 2 bytes are read.
228+ /// 2 bytes are read. The returned value is the raw offset as stored in the font file
229+ /// (typically relative to the start of the containing table). Pass it directly to
230+ /// <see cref="Seek"/> with <see cref="SeekOrigin.Begin"/> — do not add the table's
231+ /// absolute position.
175232 /// </summary>
176233 /// <returns>The 16-bit unsigned integer read.</returns>
177234 public ushort ReadOffset16 ( ) => this . ReadUInt16 ( ) ;
178235
236+ /// <summary>
237+ /// Reads a 16-bit unsigned integer from the stream and reinterprets it as the specified enum type.
238+ /// </summary>
239+ /// <typeparam name="TEnum">The enum type whose underlying type must be 16 bits.</typeparam>
240+ /// <returns>The enum value.</returns>
179241 public TEnum ReadUInt16 < TEnum > ( )
180242 where TEnum : struct , Enum
181243 {
182- TryConvert ( this . ReadUInt16 ( ) , out TEnum value ) ;
244+ _ = TryConvert ( this . ReadUInt16 ( ) , out TEnum value ) ;
183245 return value ;
184246 }
185247
186248 /// <summary>
187- /// Reads array of 16-bit unsigned integers from the stream.
249+ /// Reads an array of 16-bit unsigned integers from the stream.
188250 /// </summary>
189- /// <param name="length">The length.</param>
190- /// <returns>
191- /// The 16-bit unsigned integer read.
192- /// </returns>
251+ /// <param name="length">The number of values to read.</param>
252+ /// <returns>An array of 16-bit unsigned integers.</returns>
193253 public ushort [ ] ReadUInt16Array ( int length )
194254 {
195255 ushort [ ] data = new ushort [ length ] ;
@@ -214,12 +274,10 @@ public void ReadUInt16Array(Span<ushort> buffer)
214274 }
215275
216276 /// <summary>
217- /// Reads array or 32-bit unsigned integers from the stream.
277+ /// Reads an array of 32-bit unsigned integers from the stream.
218278 /// </summary>
219- /// <param name="length">The length.</param>
220- /// <returns>
221- /// The 32-bit unsigned integer read.
222- /// </returns>
279+ /// <param name="length">The number of values to read.</param>
280+ /// <returns>An array of 32-bit unsigned integers.</returns>
223281 public uint [ ] ReadUInt32Array ( int length )
224282 {
225283 uint [ ] data = new uint [ length ] ;
@@ -231,6 +289,11 @@ public uint[] ReadUInt32Array(int length)
231289 return data ;
232290 }
233291
292+ /// <summary>
293+ /// Reads an array of 8-bit unsigned integers (bytes) from the stream.
294+ /// </summary>
295+ /// <param name="length">The number of bytes to read.</param>
296+ /// <returns>A byte array of the requested length.</returns>
234297 public byte [ ] ReadUInt8Array ( int length )
235298 {
236299 byte [ ] data = new byte [ length ] ;
@@ -241,12 +304,10 @@ public byte[] ReadUInt8Array(int length)
241304 }
242305
243306 /// <summary>
244- /// Reads array of 16-bit unsigned integers from the stream.
307+ /// Reads an array of 16-bit signed integers from the stream.
245308 /// </summary>
246- /// <param name="length">The length.</param>
247- /// <returns>
248- /// The 16-bit signed integer read.
249- /// </returns>
309+ /// <param name="length">The number of values to read.</param>
310+ /// <returns>An array of 16-bit signed integers.</returns>
250311 public short [ ] ReadInt16Array ( int length )
251312 {
252313 short [ ] data = new short [ length ] ;
@@ -292,6 +353,14 @@ public uint ReadUInt24()
292353 return ( uint ) ( ( highByte << 16 ) | this . ReadUInt16 ( ) ) ;
293354 }
294355
356+ /// <summary>
357+ /// Reads a 24-bit unsigned integer from the stream representing an offset position.
358+ /// 3 bytes are read. The returned value is the raw offset as stored in the font file
359+ /// (typically relative to the start of the containing table). Pass it directly to
360+ /// <see cref="Seek"/> with <see cref="SeekOrigin.Begin"/> — do not add the table's
361+ /// absolute position.
362+ /// </summary>
363+ /// <returns>The 24-bit unsigned integer read.</returns>
295364 public uint ReadOffset24 ( ) => this . ReadUInt24 ( ) ;
296365
297366 /// <summary>
@@ -308,7 +377,10 @@ public uint ReadUInt32()
308377
309378 /// <summary>
310379 /// Reads a 32-bit unsigned integer from the stream representing an offset position.
311- /// 4 bytes are read.
380+ /// 4 bytes are read. The returned value is the raw offset as stored in the font file
381+ /// (typically relative to the start of the containing table). Pass it directly to
382+ /// <see cref="Seek"/> with <see cref="SeekOrigin.Begin"/> — do not add the table's
383+ /// absolute position.
312384 /// </summary>
313385 /// <returns>The 32-bit unsigned integer read.</returns>
314386 public uint ReadOffset32 ( ) => this . ReadUInt32 ( ) ;
@@ -360,9 +432,9 @@ public string ReadString(int bytesToRead, Encoding encoding)
360432 }
361433
362434 /// <summary>
363- /// Reads the uint32 string.
435+ /// Reads a 4-byte OpenType tag from the stream as a UTF-8 string.
364436 /// </summary>
365- /// <returns>a 4 character long UTF8 encoded string .</returns>
437+ /// <returns>A 4- character string representing the tag (e.g. "glyf", "GPOS") .</returns>
366438 public string ReadTag ( )
367439 {
368440 this . ReadInternal ( this . buffer , 4 ) ;
@@ -371,11 +443,15 @@ public string ReadTag()
371443 }
372444
373445 /// <summary>
374- /// Reads an offset consuming the given nuber of bytes.
446+ /// Reads an offset consuming the given number of bytes (1–4).
447+ /// The returned value is the raw offset as stored in the font file
448+ /// (typically relative to the start of the containing table). Pass it directly to
449+ /// <see cref="Seek"/> with <see cref="SeekOrigin.Begin"/> — do not add the table's
450+ /// absolute position.
375451 /// </summary>
376- /// <param name="size">The offset size in bytes.</param>
452+ /// <param name="size">The offset size in bytes (1, 2, 3, or 4) .</param>
377453 /// <returns>The 32-bit signed integer representing the offset.</returns>
378- /// <exception cref="InvalidOperationException">Size is not in range .</exception>
454+ /// <exception cref="InvalidOperationException">Thrown when <paramref name="size"/> is not 1–4 .</exception>
379455 public int ReadOffset ( int size )
380456 => size switch
381457 {
@@ -409,6 +485,7 @@ private void ReadInternal(byte[] data, int size)
409485 }
410486 }
411487
488+ /// <inheritdoc />
412489 public void Dispose ( )
413490 {
414491 if ( ! this . leaveOpen )
0 commit comments