• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  *
3  * ftstream.c
4  *
5  *   I/O stream support (body).
6  *
7  * Copyright (C) 2000-2023 by
8  * David Turner, Robert Wilhelm, and Werner Lemberg.
9  *
10  * This file is part of the FreeType project, and may only be used,
11  * modified, and distributed under the terms of the FreeType project
12  * license, LICENSE.TXT.  By continuing to use, modify, or distribute
13  * this file you indicate that you have read the license and
14  * understand and accept it fully.
15  *
16  */
17 
18 
19 #include <freetype/internal/ftstream.h>
20 #include <freetype/internal/ftdebug.h>
21 
22 
23   /**************************************************************************
24    *
25    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
26    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
27    * messages during execution.
28    */
29 #undef  FT_COMPONENT
30 #define FT_COMPONENT  stream
31 
32 
33   FT_BASE_DEF( void )
FT_Stream_OpenMemory(FT_Stream stream,const FT_Byte * base,FT_ULong size)34   FT_Stream_OpenMemory( FT_Stream       stream,
35                         const FT_Byte*  base,
36                         FT_ULong        size )
37   {
38     stream->base   = (FT_Byte*) base;
39     stream->size   = size;
40     stream->pos    = 0;
41     stream->cursor = NULL;
42     stream->read   = NULL;
43     stream->close  = NULL;
44   }
45 
46 
47   FT_BASE_DEF( void )
FT_Stream_Close(FT_Stream stream)48   FT_Stream_Close( FT_Stream  stream )
49   {
50     if ( stream && stream->close )
51       stream->close( stream );
52   }
53 
54 
55   FT_BASE_DEF( FT_Error )
FT_Stream_Seek(FT_Stream stream,FT_ULong pos)56   FT_Stream_Seek( FT_Stream  stream,
57                   FT_ULong   pos )
58   {
59     FT_Error  error = FT_Err_Ok;
60 
61 
62     if ( stream->read )
63     {
64       if ( stream->read( stream, pos, NULL, 0 ) )
65       {
66         FT_ERROR(( "FT_Stream_Seek:"
67                    " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
68                    pos, stream->size ));
69 
70         error = FT_THROW( Invalid_Stream_Operation );
71       }
72     }
73     /* note that seeking to the first position after the file is valid */
74     else if ( pos > stream->size )
75     {
76       FT_ERROR(( "FT_Stream_Seek:"
77                  " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
78                  pos, stream->size ));
79 
80       error = FT_THROW( Invalid_Stream_Operation );
81     }
82 
83     if ( !error )
84       stream->pos = pos;
85 
86     return error;
87   }
88 
89 
90   FT_BASE_DEF( FT_Error )
FT_Stream_Skip(FT_Stream stream,FT_Long distance)91   FT_Stream_Skip( FT_Stream  stream,
92                   FT_Long    distance )
93   {
94     if ( distance < 0 )
95       return FT_THROW( Invalid_Stream_Operation );
96 
97     return FT_Stream_Seek( stream, stream->pos + (FT_ULong)distance );
98   }
99 
100 
101   FT_BASE_DEF( FT_ULong )
FT_Stream_Pos(FT_Stream stream)102   FT_Stream_Pos( FT_Stream  stream )
103   {
104     return stream->pos;
105   }
106 
107 
108   FT_BASE_DEF( FT_Error )
FT_Stream_Read(FT_Stream stream,FT_Byte * buffer,FT_ULong count)109   FT_Stream_Read( FT_Stream  stream,
110                   FT_Byte*   buffer,
111                   FT_ULong   count )
112   {
113     return FT_Stream_ReadAt( stream, stream->pos, buffer, count );
114   }
115 
116 
117   FT_BASE_DEF( FT_Error )
FT_Stream_ReadAt(FT_Stream stream,FT_ULong pos,FT_Byte * buffer,FT_ULong count)118   FT_Stream_ReadAt( FT_Stream  stream,
119                     FT_ULong   pos,
120                     FT_Byte*   buffer,
121                     FT_ULong   count )
122   {
123     FT_Error  error = FT_Err_Ok;
124     FT_ULong  read_bytes;
125 
126 
127     if ( pos >= stream->size )
128     {
129       FT_ERROR(( "FT_Stream_ReadAt:"
130                  " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
131                  pos, stream->size ));
132 
133       return FT_THROW( Invalid_Stream_Operation );
134     }
135 
136     if ( stream->read )
137       read_bytes = stream->read( stream, pos, buffer, count );
138     else
139     {
140       read_bytes = stream->size - pos;
141       if ( read_bytes > count )
142         read_bytes = count;
143 
144       FT_MEM_COPY( buffer, stream->base + pos, read_bytes );
145     }
146 
147     stream->pos = pos + read_bytes;
148 
149     if ( read_bytes < count )
150     {
151       FT_ERROR(( "FT_Stream_ReadAt:"
152                  " invalid read; expected %lu bytes, got %lu\n",
153                  count, read_bytes ));
154 
155       error = FT_THROW( Invalid_Stream_Operation );
156     }
157 
158     return error;
159   }
160 
161 
162   FT_BASE_DEF( FT_ULong )
FT_Stream_TryRead(FT_Stream stream,FT_Byte * buffer,FT_ULong count)163   FT_Stream_TryRead( FT_Stream  stream,
164                      FT_Byte*   buffer,
165                      FT_ULong   count )
166   {
167     FT_ULong  read_bytes = 0;
168 
169 
170     if ( stream->pos >= stream->size )
171       goto Exit;
172 
173     if ( stream->read )
174       read_bytes = stream->read( stream, stream->pos, buffer, count );
175     else
176     {
177       read_bytes = stream->size - stream->pos;
178       if ( read_bytes > count )
179         read_bytes = count;
180 
181       FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes );
182     }
183 
184     stream->pos += read_bytes;
185 
186   Exit:
187     return read_bytes;
188   }
189 
190 
191   FT_BASE_DEF( FT_Error )
FT_Stream_ExtractFrame(FT_Stream stream,FT_ULong count,FT_Byte ** pbytes)192   FT_Stream_ExtractFrame( FT_Stream  stream,
193                           FT_ULong   count,
194                           FT_Byte**  pbytes )
195   {
196     FT_Error  error;
197 
198 
199     error = FT_Stream_EnterFrame( stream, count );
200     if ( !error )
201     {
202       *pbytes = (FT_Byte*)stream->cursor;
203 
204       /* equivalent to FT_Stream_ExitFrame(), with no memory block release */
205       stream->cursor = NULL;
206       stream->limit  = NULL;
207     }
208 
209     return error;
210   }
211 
212 
213   FT_BASE_DEF( void )
FT_Stream_ReleaseFrame(FT_Stream stream,FT_Byte ** pbytes)214   FT_Stream_ReleaseFrame( FT_Stream  stream,
215                           FT_Byte**  pbytes )
216   {
217     if ( stream && stream->read )
218     {
219       FT_Memory  memory = stream->memory;
220 
221 
222 #ifdef FT_DEBUG_MEMORY
223       ft_mem_free( memory, *pbytes );
224 #else
225       FT_FREE( *pbytes );
226 #endif
227     }
228 
229     *pbytes = NULL;
230   }
231 
232 
233   FT_BASE_DEF( FT_Error )
FT_Stream_EnterFrame(FT_Stream stream,FT_ULong count)234   FT_Stream_EnterFrame( FT_Stream  stream,
235                         FT_ULong   count )
236   {
237     FT_Error  error = FT_Err_Ok;
238     FT_ULong  read_bytes;
239 
240 
241     FT_TRACE7(( "FT_Stream_EnterFrame: %ld bytes\n", count ));
242 
243     /* check for nested frame access */
244     FT_ASSERT( stream && stream->cursor == 0 );
245 
246     if ( stream->read )
247     {
248       /* allocate the frame in memory */
249       FT_Memory  memory = stream->memory;
250 
251 
252       /* simple sanity check */
253       if ( count > stream->size )
254       {
255         FT_ERROR(( "FT_Stream_EnterFrame:"
256                    " frame size (%lu) larger than stream size (%lu)\n",
257                    count, stream->size ));
258 
259         error = FT_THROW( Invalid_Stream_Operation );
260         goto Exit;
261       }
262 
263 #ifdef FT_DEBUG_MEMORY
264       /* assume `ft_debug_file_` and `ft_debug_lineno_` are already set */
265       stream->base = (unsigned char*)ft_mem_qalloc( memory,
266                                                     (FT_Long)count,
267                                                     &error );
268       if ( error )
269         goto Exit;
270 #else
271       if ( FT_QALLOC( stream->base, count ) )
272         goto Exit;
273 #endif
274       /* read it */
275       read_bytes = stream->read( stream, stream->pos,
276                                  stream->base, count );
277       if ( read_bytes < count )
278       {
279         FT_ERROR(( "FT_Stream_EnterFrame:"
280                    " invalid read; expected %lu bytes, got %lu\n",
281                    count, read_bytes ));
282 
283         FT_FREE( stream->base );
284         error = FT_THROW( Invalid_Stream_Operation );
285       }
286 
287       stream->cursor = stream->base;
288       stream->limit  = FT_OFFSET( stream->cursor, count );
289       stream->pos   += read_bytes;
290     }
291     else
292     {
293       /* check current and new position */
294       if ( stream->pos >= stream->size        ||
295            stream->size - stream->pos < count )
296       {
297         FT_ERROR(( "FT_Stream_EnterFrame:"
298                    " invalid i/o; pos = 0x%lx, count = %lu, size = 0x%lx\n",
299                    stream->pos, count, stream->size ));
300 
301         error = FT_THROW( Invalid_Stream_Operation );
302         goto Exit;
303       }
304 
305       /* set cursor */
306       stream->cursor = stream->base + stream->pos;
307       stream->limit  = stream->cursor + count;
308       stream->pos   += count;
309     }
310 
311   Exit:
312     return error;
313   }
314 
315 
316   FT_BASE_DEF( void )
FT_Stream_ExitFrame(FT_Stream stream)317   FT_Stream_ExitFrame( FT_Stream  stream )
318   {
319     /* IMPORTANT: The assertion stream->cursor != 0 was removed, given    */
320     /*            that it is possible to access a frame of length 0 in    */
321     /*            some weird fonts (usually, when accessing an array of   */
322     /*            0 records, like in some strange kern tables).           */
323     /*                                                                    */
324     /*  In this case, the loader code handles the 0-length table          */
325     /*  gracefully; however, stream.cursor is really set to 0 by the      */
326     /*  FT_Stream_EnterFrame() call, and this is not an error.            */
327 
328     FT_TRACE7(( "FT_Stream_ExitFrame\n" ));
329 
330     FT_ASSERT( stream );
331 
332     if ( stream->read )
333     {
334       FT_Memory  memory = stream->memory;
335 
336 
337 #ifdef FT_DEBUG_MEMORY
338       ft_mem_free( memory, stream->base );
339       stream->base = NULL;
340 #else
341       FT_FREE( stream->base );
342 #endif
343     }
344 
345     stream->cursor = NULL;
346     stream->limit  = NULL;
347   }
348 
349 
350   FT_BASE_DEF( FT_Byte )
FT_Stream_GetByte(FT_Stream stream)351   FT_Stream_GetByte( FT_Stream  stream )
352   {
353     FT_Byte  result;
354 
355 
356     FT_ASSERT( stream && stream->cursor );
357 
358     result = 0;
359     if ( stream->cursor < stream->limit )
360       result = *stream->cursor++;
361 
362     return result;
363   }
364 
365 
366   FT_BASE_DEF( FT_UInt16 )
FT_Stream_GetUShort(FT_Stream stream)367   FT_Stream_GetUShort( FT_Stream  stream )
368   {
369     FT_Byte*   p;
370     FT_UInt16  result;
371 
372 
373     FT_ASSERT( stream && stream->cursor );
374 
375     result         = 0;
376     p              = stream->cursor;
377     if ( p + 1 < stream->limit )
378       result       = FT_NEXT_USHORT( p );
379     stream->cursor = p;
380 
381     return result;
382   }
383 
384 
385   FT_BASE_DEF( FT_UInt16 )
FT_Stream_GetUShortLE(FT_Stream stream)386   FT_Stream_GetUShortLE( FT_Stream  stream )
387   {
388     FT_Byte*   p;
389     FT_UInt16  result;
390 
391 
392     FT_ASSERT( stream && stream->cursor );
393 
394     result         = 0;
395     p              = stream->cursor;
396     if ( p + 1 < stream->limit )
397       result       = FT_NEXT_USHORT_LE( p );
398     stream->cursor = p;
399 
400     return result;
401   }
402 
403 
404   FT_BASE_DEF( FT_UInt32 )
FT_Stream_GetUOffset(FT_Stream stream)405   FT_Stream_GetUOffset( FT_Stream  stream )
406   {
407     FT_Byte*  p;
408     FT_UInt32 result;
409 
410 
411     FT_ASSERT( stream && stream->cursor );
412 
413     result         = 0;
414     p              = stream->cursor;
415     if ( p + 2 < stream->limit )
416       result       = FT_NEXT_UOFF3( p );
417     stream->cursor = p;
418     return result;
419   }
420 
421 
422   FT_BASE_DEF( FT_UInt32 )
FT_Stream_GetULong(FT_Stream stream)423   FT_Stream_GetULong( FT_Stream  stream )
424   {
425     FT_Byte*  p;
426     FT_UInt32 result;
427 
428 
429     FT_ASSERT( stream && stream->cursor );
430 
431     result         = 0;
432     p              = stream->cursor;
433     if ( p + 3 < stream->limit )
434       result       = FT_NEXT_ULONG( p );
435     stream->cursor = p;
436     return result;
437   }
438 
439 
440   FT_BASE_DEF( FT_UInt32 )
FT_Stream_GetULongLE(FT_Stream stream)441   FT_Stream_GetULongLE( FT_Stream  stream )
442   {
443     FT_Byte*  p;
444     FT_UInt32 result;
445 
446 
447     FT_ASSERT( stream && stream->cursor );
448 
449     result         = 0;
450     p              = stream->cursor;
451     if ( p + 3 < stream->limit )
452       result       = FT_NEXT_ULONG_LE( p );
453     stream->cursor = p;
454     return result;
455   }
456 
457 
458   FT_BASE_DEF( FT_Byte )
FT_Stream_ReadByte(FT_Stream stream,FT_Error * error)459   FT_Stream_ReadByte( FT_Stream  stream,
460                       FT_Error*  error )
461   {
462     FT_Byte  result = 0;
463 
464 
465     FT_ASSERT( stream );
466 
467     if ( stream->pos < stream->size )
468     {
469       if ( stream->read )
470       {
471         if ( stream->read( stream, stream->pos, &result, 1L ) != 1L )
472           goto Fail;
473       }
474       else
475         result = stream->base[stream->pos];
476     }
477     else
478       goto Fail;
479 
480     stream->pos++;
481 
482     *error = FT_Err_Ok;
483 
484     return result;
485 
486   Fail:
487     *error = FT_THROW( Invalid_Stream_Operation );
488     FT_ERROR(( "FT_Stream_ReadByte:"
489                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
490                stream->pos, stream->size ));
491 
492     return result;
493   }
494 
495 
496   FT_BASE_DEF( FT_UInt16 )
FT_Stream_ReadUShort(FT_Stream stream,FT_Error * error)497   FT_Stream_ReadUShort( FT_Stream  stream,
498                         FT_Error*  error )
499   {
500     FT_Byte    reads[2];
501     FT_Byte*   p;
502     FT_UInt16  result = 0;
503 
504 
505     FT_ASSERT( stream );
506 
507     if ( stream->pos + 1 < stream->size )
508     {
509       if ( stream->read )
510       {
511         if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
512           goto Fail;
513 
514         p = reads;
515       }
516       else
517         p = stream->base + stream->pos;
518 
519       if ( p )
520         result = FT_NEXT_USHORT( p );
521     }
522     else
523       goto Fail;
524 
525     stream->pos += 2;
526 
527     *error = FT_Err_Ok;
528 
529     return result;
530 
531   Fail:
532     *error = FT_THROW( Invalid_Stream_Operation );
533     FT_ERROR(( "FT_Stream_ReadUShort:"
534                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
535                stream->pos, stream->size ));
536 
537     return result;
538   }
539 
540 
541   FT_BASE_DEF( FT_UInt16 )
FT_Stream_ReadUShortLE(FT_Stream stream,FT_Error * error)542   FT_Stream_ReadUShortLE( FT_Stream  stream,
543                           FT_Error*  error )
544   {
545     FT_Byte    reads[2];
546     FT_Byte*   p;
547     FT_UInt16  result = 0;
548 
549 
550     FT_ASSERT( stream );
551 
552     if ( stream->pos + 1 < stream->size )
553     {
554       if ( stream->read )
555       {
556         if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
557           goto Fail;
558 
559         p = reads;
560       }
561       else
562         p = stream->base + stream->pos;
563 
564       if ( p )
565         result = FT_NEXT_USHORT_LE( p );
566     }
567     else
568       goto Fail;
569 
570     stream->pos += 2;
571 
572     *error = FT_Err_Ok;
573 
574     return result;
575 
576   Fail:
577     *error = FT_THROW( Invalid_Stream_Operation );
578     FT_ERROR(( "FT_Stream_ReadUShortLE:"
579                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
580                stream->pos, stream->size ));
581 
582     return result;
583   }
584 
585 
586   FT_BASE_DEF( FT_ULong )
FT_Stream_ReadUOffset(FT_Stream stream,FT_Error * error)587   FT_Stream_ReadUOffset( FT_Stream  stream,
588                          FT_Error*  error )
589   {
590     FT_Byte   reads[3];
591     FT_Byte*  p;
592     FT_ULong  result = 0;
593 
594 
595     FT_ASSERT( stream );
596 
597     if ( stream->pos + 2 < stream->size )
598     {
599       if ( stream->read )
600       {
601         if (stream->read( stream, stream->pos, reads, 3L ) != 3L )
602           goto Fail;
603 
604         p = reads;
605       }
606       else
607         p = stream->base + stream->pos;
608 
609       if ( p )
610         result = FT_NEXT_UOFF3( p );
611     }
612     else
613       goto Fail;
614 
615     stream->pos += 3;
616 
617     *error = FT_Err_Ok;
618 
619     return result;
620 
621   Fail:
622     *error = FT_THROW( Invalid_Stream_Operation );
623     FT_ERROR(( "FT_Stream_ReadUOffset:"
624                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
625                stream->pos, stream->size ));
626 
627     return result;
628   }
629 
630 
631   FT_BASE_DEF( FT_UInt32 )
FT_Stream_ReadULong(FT_Stream stream,FT_Error * error)632   FT_Stream_ReadULong( FT_Stream  stream,
633                        FT_Error*  error )
634   {
635     FT_Byte   reads[4];
636     FT_Byte*  p;
637     FT_UInt32 result = 0;
638 
639 
640     FT_ASSERT( stream );
641 
642     if ( stream->pos + 3 < stream->size )
643     {
644       if ( stream->read )
645       {
646         if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
647           goto Fail;
648 
649         p = reads;
650       }
651       else
652         p = stream->base + stream->pos;
653 
654       if ( p )
655         result = FT_NEXT_ULONG( p );
656     }
657     else
658       goto Fail;
659 
660     stream->pos += 4;
661 
662     *error = FT_Err_Ok;
663 
664     return result;
665 
666   Fail:
667     *error = FT_THROW( Invalid_Stream_Operation );
668     FT_ERROR(( "FT_Stream_ReadULong:"
669                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
670                stream->pos, stream->size ));
671 
672     return result;
673   }
674 
675 
676   FT_BASE_DEF( FT_UInt32 )
FT_Stream_ReadULongLE(FT_Stream stream,FT_Error * error)677   FT_Stream_ReadULongLE( FT_Stream  stream,
678                          FT_Error*  error )
679   {
680     FT_Byte   reads[4];
681     FT_Byte*  p;
682     FT_UInt32 result = 0;
683 
684 
685     FT_ASSERT( stream );
686 
687     if ( stream->pos + 3 < stream->size )
688     {
689       if ( stream->read )
690       {
691         if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
692           goto Fail;
693 
694         p = reads;
695       }
696       else
697         p = stream->base + stream->pos;
698 
699       if ( p )
700         result = FT_NEXT_ULONG_LE( p );
701     }
702     else
703       goto Fail;
704 
705     stream->pos += 4;
706 
707     *error = FT_Err_Ok;
708 
709     return result;
710 
711   Fail:
712     *error = FT_THROW( Invalid_Stream_Operation );
713     FT_ERROR(( "FT_Stream_ReadULongLE:"
714                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
715                stream->pos, stream->size ));
716 
717     return result;
718   }
719 
720 
721   FT_BASE_DEF( FT_Error )
FT_Stream_ReadFields(FT_Stream stream,const FT_Frame_Field * fields,void * structure)722   FT_Stream_ReadFields( FT_Stream              stream,
723                         const FT_Frame_Field*  fields,
724                         void*                  structure )
725   {
726     FT_Error  error;
727     FT_Bool   frame_accessed = 0;
728     FT_Byte*  cursor;
729 
730 
731     if ( !fields )
732       return FT_THROW( Invalid_Argument );
733 
734     if ( !stream )
735       return FT_THROW( Invalid_Stream_Handle );
736 
737     cursor = stream->cursor;
738 
739     error = FT_Err_Ok;
740     do
741     {
742       FT_ULong  value;
743       FT_Int    sign_shift;
744       FT_Byte*  p;
745 
746 
747       switch ( fields->value )
748       {
749       case ft_frame_start:  /* access a new frame */
750         error = FT_Stream_EnterFrame( stream, fields->offset );
751         if ( error )
752           goto Exit;
753 
754         frame_accessed = 1;
755         cursor         = stream->cursor;
756         fields++;
757         continue;  /* loop! */
758 
759       case ft_frame_bytes:  /* read a byte sequence */
760       case ft_frame_skip:   /* skip some bytes      */
761         {
762           FT_UInt  len = fields->size;
763 
764 
765           if ( cursor + len > stream->limit )
766           {
767             error = FT_THROW( Invalid_Stream_Operation );
768             goto Exit;
769           }
770 
771           if ( fields->value == ft_frame_bytes )
772           {
773             p = (FT_Byte*)structure + fields->offset;
774             FT_MEM_COPY( p, cursor, len );
775           }
776           cursor += len;
777           fields++;
778           continue;
779         }
780 
781       case ft_frame_byte:
782       case ft_frame_schar:  /* read a single byte */
783         value = FT_NEXT_BYTE( cursor );
784         sign_shift = 24;
785         break;
786 
787       case ft_frame_short_be:
788       case ft_frame_ushort_be:  /* read a 2-byte big-endian short */
789         value = FT_NEXT_USHORT( cursor );
790         sign_shift = 16;
791         break;
792 
793       case ft_frame_short_le:
794       case ft_frame_ushort_le:  /* read a 2-byte little-endian short */
795         value = FT_NEXT_USHORT_LE( cursor );
796         sign_shift = 16;
797         break;
798 
799       case ft_frame_long_be:
800       case ft_frame_ulong_be:  /* read a 4-byte big-endian long */
801         value = FT_NEXT_ULONG( cursor );
802         sign_shift = 0;
803         break;
804 
805       case ft_frame_long_le:
806       case ft_frame_ulong_le:  /* read a 4-byte little-endian long */
807         value = FT_NEXT_ULONG_LE( cursor );
808         sign_shift = 0;
809         break;
810 
811       case ft_frame_off3_be:
812       case ft_frame_uoff3_be:  /* read a 3-byte big-endian long */
813         value = FT_NEXT_UOFF3( cursor );
814         sign_shift = 8;
815         break;
816 
817       case ft_frame_off3_le:
818       case ft_frame_uoff3_le:  /* read a 3-byte little-endian long */
819         value = FT_NEXT_UOFF3_LE( cursor );
820         sign_shift = 8;
821         break;
822 
823       default:
824         /* otherwise, exit the loop */
825         stream->cursor = cursor;
826         goto Exit;
827       }
828 
829       /* now, compute the signed value is necessary */
830       if ( fields->value & FT_FRAME_OP_SIGNED )
831         value = (FT_ULong)( (FT_Int32)( value << sign_shift ) >> sign_shift );
832 
833       /* finally, store the value in the object */
834 
835       p = (FT_Byte*)structure + fields->offset;
836       switch ( fields->size )
837       {
838       case ( 8 / FT_CHAR_BIT ):
839         *(FT_Byte*)p = (FT_Byte)value;
840         break;
841 
842       case ( 16 / FT_CHAR_BIT ):
843         *(FT_UShort*)p = (FT_UShort)value;
844         break;
845 
846       case ( 32 / FT_CHAR_BIT ):
847         *(FT_UInt32*)p = (FT_UInt32)value;
848         break;
849 
850       default:  /* for 64-bit systems */
851         *(FT_ULong*)p = (FT_ULong)value;
852       }
853 
854       /* go to next field */
855       fields++;
856     }
857     while ( 1 );
858 
859   Exit:
860     /* close the frame if it was opened by this read */
861     if ( frame_accessed )
862       FT_Stream_ExitFrame( stream );
863 
864     return error;
865   }
866 
867 
868 /* END */
869