• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  *
3  * ftstream.c
4  *
5  *   I/O stream support (body).
6  *
7  * Copyright (C) 2000-2019 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 <ft2build.h>
20 #include FT_INTERNAL_STREAM_H
21 #include FT_INTERNAL_DEBUG_H
22 
23 
24   /**************************************************************************
25    *
26    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
27    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
28    * messages during execution.
29    */
30 #undef  FT_COMPONENT
31 #define FT_COMPONENT  stream
32 
33 
34   FT_BASE_DEF( void )
FT_Stream_OpenMemory(FT_Stream stream,const FT_Byte * base,FT_ULong size)35   FT_Stream_OpenMemory( FT_Stream       stream,
36                         const FT_Byte*  base,
37                         FT_ULong        size )
38   {
39     stream->base   = (FT_Byte*) base;
40     stream->size   = size;
41     stream->pos    = 0;
42     stream->cursor = NULL;
43     stream->read   = NULL;
44     stream->close  = NULL;
45   }
46 
47 
48   FT_BASE_DEF( void )
FT_Stream_Close(FT_Stream stream)49   FT_Stream_Close( FT_Stream  stream )
50   {
51     if ( stream && stream->close )
52       stream->close( stream );
53   }
54 
55 
56   FT_BASE_DEF( FT_Error )
FT_Stream_Seek(FT_Stream stream,FT_ULong pos)57   FT_Stream_Seek( FT_Stream  stream,
58                   FT_ULong   pos )
59   {
60     FT_Error  error = FT_Err_Ok;
61 
62 
63     if ( stream->read )
64     {
65       if ( stream->read( stream, pos, 0, 0 ) )
66       {
67         FT_ERROR(( "FT_Stream_Seek:"
68                    " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
69                    pos, stream->size ));
70 
71         error = FT_THROW( Invalid_Stream_Operation );
72       }
73     }
74     /* note that seeking to the first position after the file is valid */
75     else if ( pos > stream->size )
76     {
77       FT_ERROR(( "FT_Stream_Seek:"
78                  " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
79                  pos, stream->size ));
80 
81       error = FT_THROW( Invalid_Stream_Operation );
82     }
83 
84     if ( !error )
85       stream->pos = pos;
86 
87     return error;
88   }
89 
90 
91   FT_BASE_DEF( FT_Error )
FT_Stream_Skip(FT_Stream stream,FT_Long distance)92   FT_Stream_Skip( FT_Stream  stream,
93                   FT_Long    distance )
94   {
95     if ( distance < 0 )
96       return FT_THROW( Invalid_Stream_Operation );
97 
98     return FT_Stream_Seek( stream, stream->pos + (FT_ULong)distance );
99   }
100 
101 
102   FT_BASE_DEF( FT_ULong )
FT_Stream_Pos(FT_Stream stream)103   FT_Stream_Pos( FT_Stream  stream )
104   {
105     return stream->pos;
106   }
107 
108 
109   FT_BASE_DEF( FT_Error )
FT_Stream_Read(FT_Stream stream,FT_Byte * buffer,FT_ULong count)110   FT_Stream_Read( FT_Stream  stream,
111                   FT_Byte*   buffer,
112                   FT_ULong   count )
113   {
114     return FT_Stream_ReadAt( stream, stream->pos, buffer, count );
115   }
116 
117 
118   FT_BASE_DEF( FT_Error )
FT_Stream_ReadAt(FT_Stream stream,FT_ULong pos,FT_Byte * buffer,FT_ULong count)119   FT_Stream_ReadAt( FT_Stream  stream,
120                     FT_ULong   pos,
121                     FT_Byte*   buffer,
122                     FT_ULong   count )
123   {
124     FT_Error  error = FT_Err_Ok;
125     FT_ULong  read_bytes;
126 
127 
128     if ( pos >= stream->size )
129     {
130       FT_ERROR(( "FT_Stream_ReadAt:"
131                  " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
132                  pos, stream->size ));
133 
134       return FT_THROW( Invalid_Stream_Operation );
135     }
136 
137     if ( stream->read )
138       read_bytes = stream->read( stream, pos, buffer, count );
139     else
140     {
141       read_bytes = stream->size - pos;
142       if ( read_bytes > count )
143         read_bytes = count;
144 
145       FT_MEM_COPY( buffer, stream->base + pos, read_bytes );
146     }
147 
148     stream->pos = pos + read_bytes;
149 
150     if ( read_bytes < count )
151     {
152       FT_ERROR(( "FT_Stream_ReadAt:"
153                  " invalid read; expected %lu bytes, got %lu\n",
154                  count, read_bytes ));
155 
156       error = FT_THROW( Invalid_Stream_Operation );
157     }
158 
159     return error;
160   }
161 
162 
163   FT_BASE_DEF( FT_ULong )
FT_Stream_TryRead(FT_Stream stream,FT_Byte * buffer,FT_ULong count)164   FT_Stream_TryRead( FT_Stream  stream,
165                      FT_Byte*   buffer,
166                      FT_ULong   count )
167   {
168     FT_ULong  read_bytes = 0;
169 
170 
171     if ( stream->pos >= stream->size )
172       goto Exit;
173 
174     if ( stream->read )
175       read_bytes = stream->read( stream, stream->pos, buffer, count );
176     else
177     {
178       read_bytes = stream->size - stream->pos;
179       if ( read_bytes > count )
180         read_bytes = count;
181 
182       FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes );
183     }
184 
185     stream->pos += read_bytes;
186 
187   Exit:
188     return read_bytes;
189   }
190 
191 
192   FT_BASE_DEF( FT_Error )
FT_Stream_ExtractFrame(FT_Stream stream,FT_ULong count,FT_Byte ** pbytes)193   FT_Stream_ExtractFrame( FT_Stream  stream,
194                           FT_ULong   count,
195                           FT_Byte**  pbytes )
196   {
197     FT_Error  error;
198 
199 
200     error = FT_Stream_EnterFrame( stream, count );
201     if ( !error )
202     {
203       *pbytes = (FT_Byte*)stream->cursor;
204 
205       /* equivalent to FT_Stream_ExitFrame(), with no memory block release */
206       stream->cursor = NULL;
207       stream->limit  = NULL;
208     }
209 
210     return error;
211   }
212 
213 
214   FT_BASE_DEF( void )
FT_Stream_ReleaseFrame(FT_Stream stream,FT_Byte ** pbytes)215   FT_Stream_ReleaseFrame( FT_Stream  stream,
216                           FT_Byte**  pbytes )
217   {
218     if ( stream && stream->read )
219     {
220       FT_Memory  memory = stream->memory;
221 
222 
223 #ifdef FT_DEBUG_MEMORY
224       ft_mem_free( memory, *pbytes );
225 #else
226       FT_FREE( *pbytes );
227 #endif
228     }
229 
230     *pbytes = NULL;
231   }
232 
233 
234   FT_BASE_DEF( FT_Error )
FT_Stream_EnterFrame(FT_Stream stream,FT_ULong count)235   FT_Stream_EnterFrame( FT_Stream  stream,
236                         FT_ULong   count )
237   {
238     FT_Error  error = FT_Err_Ok;
239     FT_ULong  read_bytes;
240 
241 
242     FT_TRACE7(( "FT_Stream_EnterFrame: %ld bytes\n", count ));
243 
244     /* check for nested frame access */
245     FT_ASSERT( stream && stream->cursor == 0 );
246 
247     if ( stream->read )
248     {
249       /* allocate the frame in memory */
250       FT_Memory  memory = stream->memory;
251 
252 
253       /* simple sanity check */
254       if ( count > stream->size )
255       {
256         FT_ERROR(( "FT_Stream_EnterFrame:"
257                    " frame size (%lu) larger than stream size (%lu)\n",
258                    count, stream->size ));
259 
260         error = FT_THROW( Invalid_Stream_Operation );
261         goto Exit;
262       }
263 
264 #ifdef FT_DEBUG_MEMORY
265       /* assume _ft_debug_file and _ft_debug_lineno are already set */
266       stream->base = (unsigned char*)ft_mem_qalloc( memory,
267                                                     (FT_Long)count,
268                                                     &error );
269       if ( error )
270         goto Exit;
271 #else
272       if ( FT_QALLOC( stream->base, count ) )
273         goto Exit;
274 #endif
275       /* read it */
276       read_bytes = stream->read( stream, stream->pos,
277                                  stream->base, count );
278       if ( read_bytes < count )
279       {
280         FT_ERROR(( "FT_Stream_EnterFrame:"
281                    " invalid read; expected %lu bytes, got %lu\n",
282                    count, read_bytes ));
283 
284         FT_FREE( stream->base );
285         error = FT_THROW( Invalid_Stream_Operation );
286       }
287 
288       stream->cursor = stream->base;
289       stream->limit  = stream->cursor + count;
290       stream->pos   += read_bytes;
291     }
292     else
293     {
294       /* check current and new position */
295       if ( stream->pos >= stream->size        ||
296            stream->size - stream->pos < count )
297       {
298         FT_ERROR(( "FT_Stream_EnterFrame:"
299                    " invalid i/o; pos = 0x%lx, count = %lu, size = 0x%lx\n",
300                    stream->pos, count, stream->size ));
301 
302         error = FT_THROW( Invalid_Stream_Operation );
303         goto Exit;
304       }
305 
306       /* set cursor */
307       stream->cursor = stream->base + stream->pos;
308       stream->limit  = stream->cursor + count;
309       stream->pos   += count;
310     }
311 
312   Exit:
313     return error;
314   }
315 
316 
317   FT_BASE_DEF( void )
FT_Stream_ExitFrame(FT_Stream stream)318   FT_Stream_ExitFrame( FT_Stream  stream )
319   {
320     /* IMPORTANT: The assertion stream->cursor != 0 was removed, given    */
321     /*            that it is possible to access a frame of length 0 in    */
322     /*            some weird fonts (usually, when accessing an array of   */
323     /*            0 records, like in some strange kern tables).           */
324     /*                                                                    */
325     /*  In this case, the loader code handles the 0-length table          */
326     /*  gracefully; however, stream.cursor is really set to 0 by the      */
327     /*  FT_Stream_EnterFrame() call, and this is not an error.            */
328 
329     FT_TRACE7(( "FT_Stream_ExitFrame\n" ));
330 
331     FT_ASSERT( stream );
332 
333     if ( stream->read )
334     {
335       FT_Memory  memory = stream->memory;
336 
337 
338 #ifdef FT_DEBUG_MEMORY
339       ft_mem_free( memory, stream->base );
340       stream->base = NULL;
341 #else
342       FT_FREE( stream->base );
343 #endif
344     }
345 
346     stream->cursor = NULL;
347     stream->limit  = NULL;
348   }
349 
350 
351   FT_BASE_DEF( FT_Char )
FT_Stream_GetChar(FT_Stream stream)352   FT_Stream_GetChar( FT_Stream  stream )
353   {
354     FT_Char  result;
355 
356 
357     FT_ASSERT( stream && stream->cursor );
358 
359     result = 0;
360     if ( stream->cursor < stream->limit )
361       result = (FT_Char)*stream->cursor++;
362 
363     return result;
364   }
365 
366 
367   FT_BASE_DEF( FT_UShort )
FT_Stream_GetUShort(FT_Stream stream)368   FT_Stream_GetUShort( FT_Stream  stream )
369   {
370     FT_Byte*   p;
371     FT_UShort  result;
372 
373 
374     FT_ASSERT( stream && stream->cursor );
375 
376     result         = 0;
377     p              = stream->cursor;
378     if ( p + 1 < stream->limit )
379       result       = FT_NEXT_USHORT( p );
380     stream->cursor = p;
381 
382     return result;
383   }
384 
385 
386   FT_BASE_DEF( FT_UShort )
FT_Stream_GetUShortLE(FT_Stream stream)387   FT_Stream_GetUShortLE( FT_Stream  stream )
388   {
389     FT_Byte*   p;
390     FT_UShort  result;
391 
392 
393     FT_ASSERT( stream && stream->cursor );
394 
395     result         = 0;
396     p              = stream->cursor;
397     if ( p + 1 < stream->limit )
398       result       = FT_NEXT_USHORT_LE( p );
399     stream->cursor = p;
400 
401     return result;
402   }
403 
404 
405   FT_BASE_DEF( FT_ULong )
FT_Stream_GetUOffset(FT_Stream stream)406   FT_Stream_GetUOffset( FT_Stream  stream )
407   {
408     FT_Byte*  p;
409     FT_ULong  result;
410 
411 
412     FT_ASSERT( stream && stream->cursor );
413 
414     result         = 0;
415     p              = stream->cursor;
416     if ( p + 2 < stream->limit )
417       result       = FT_NEXT_UOFF3( p );
418     stream->cursor = p;
419     return result;
420   }
421 
422 
423   FT_BASE_DEF( FT_ULong )
FT_Stream_GetULong(FT_Stream stream)424   FT_Stream_GetULong( FT_Stream  stream )
425   {
426     FT_Byte*  p;
427     FT_ULong  result;
428 
429 
430     FT_ASSERT( stream && stream->cursor );
431 
432     result         = 0;
433     p              = stream->cursor;
434     if ( p + 3 < stream->limit )
435       result       = FT_NEXT_ULONG( p );
436     stream->cursor = p;
437     return result;
438   }
439 
440 
441   FT_BASE_DEF( FT_ULong )
FT_Stream_GetULongLE(FT_Stream stream)442   FT_Stream_GetULongLE( FT_Stream  stream )
443   {
444     FT_Byte*  p;
445     FT_ULong  result;
446 
447 
448     FT_ASSERT( stream && stream->cursor );
449 
450     result         = 0;
451     p              = stream->cursor;
452     if ( p + 3 < stream->limit )
453       result       = FT_NEXT_ULONG_LE( p );
454     stream->cursor = p;
455     return result;
456   }
457 
458 
459   FT_BASE_DEF( FT_Char )
FT_Stream_ReadChar(FT_Stream stream,FT_Error * error)460   FT_Stream_ReadChar( FT_Stream  stream,
461                       FT_Error*  error )
462   {
463     FT_Byte  result = 0;
464 
465 
466     FT_ASSERT( stream );
467 
468     *error = FT_Err_Ok;
469 
470     if ( stream->read )
471     {
472       if ( stream->read( stream, stream->pos, &result, 1L ) != 1L )
473         goto Fail;
474     }
475     else
476     {
477       if ( stream->pos < stream->size )
478         result = stream->base[stream->pos];
479       else
480         goto Fail;
481     }
482     stream->pos++;
483 
484     return (FT_Char)result;
485 
486   Fail:
487     *error = FT_THROW( Invalid_Stream_Operation );
488     FT_ERROR(( "FT_Stream_ReadChar:"
489                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
490                stream->pos, stream->size ));
491 
492     return 0;
493   }
494 
495 
496   FT_BASE_DEF( FT_UShort )
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      = 0;
502     FT_UShort  result = 0;
503 
504 
505     FT_ASSERT( stream );
506 
507     *error = FT_Err_Ok;
508 
509     if ( stream->pos + 1 < stream->size )
510     {
511       if ( stream->read )
512       {
513         if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
514           goto Fail;
515 
516         p = reads;
517       }
518       else
519         p = stream->base + stream->pos;
520 
521       if ( p )
522         result = FT_NEXT_USHORT( p );
523     }
524     else
525       goto Fail;
526 
527     stream->pos += 2;
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 0;
538   }
539 
540 
541   FT_BASE_DEF( FT_UShort )
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      = 0;
547     FT_UShort  result = 0;
548 
549 
550     FT_ASSERT( stream );
551 
552     *error = FT_Err_Ok;
553 
554     if ( stream->pos + 1 < stream->size )
555     {
556       if ( stream->read )
557       {
558         if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
559           goto Fail;
560 
561         p = reads;
562       }
563       else
564         p = stream->base + stream->pos;
565 
566       if ( p )
567         result = FT_NEXT_USHORT_LE( p );
568     }
569     else
570       goto Fail;
571 
572     stream->pos += 2;
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 0;
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      = 0;
592     FT_ULong  result = 0;
593 
594 
595     FT_ASSERT( stream );
596 
597     *error = FT_Err_Ok;
598 
599     if ( stream->pos + 2 < stream->size )
600     {
601       if ( stream->read )
602       {
603         if (stream->read( stream, stream->pos, reads, 3L ) != 3L )
604           goto Fail;
605 
606         p = reads;
607       }
608       else
609         p = stream->base + stream->pos;
610 
611       if ( p )
612         result = FT_NEXT_UOFF3( p );
613     }
614     else
615       goto Fail;
616 
617     stream->pos += 3;
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 0;
628   }
629 
630 
631   FT_BASE_DEF( FT_ULong )
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      = 0;
637     FT_ULong  result = 0;
638 
639 
640     FT_ASSERT( stream );
641 
642     *error = FT_Err_Ok;
643 
644     if ( stream->pos + 3 < stream->size )
645     {
646       if ( stream->read )
647       {
648         if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
649           goto Fail;
650 
651         p = reads;
652       }
653       else
654         p = stream->base + stream->pos;
655 
656       if ( p )
657         result = FT_NEXT_ULONG( p );
658     }
659     else
660       goto Fail;
661 
662     stream->pos += 4;
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 0;
673   }
674 
675 
676   FT_BASE_DEF( FT_ULong )
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      = 0;
682     FT_ULong  result = 0;
683 
684 
685     FT_ASSERT( stream );
686 
687     *error = FT_Err_Ok;
688 
689     if ( stream->pos + 3 < stream->size )
690     {
691       if ( stream->read )
692       {
693         if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
694           goto Fail;
695 
696         p = reads;
697       }
698       else
699         p = stream->base + stream->pos;
700 
701       if ( p )
702         result = FT_NEXT_ULONG_LE( p );
703     }
704     else
705       goto Fail;
706 
707     stream->pos += 4;
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 0;
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