source: trunk/poppler/freetype2/src/psaux/psobjs.c @ 251

Last change on this file since 251 was 251, checked in by Eugene Romanenko, 13 years ago

PDF plugin: freetype library updated to version 2.3.5

File size: 48.1 KB
Line 
1/***************************************************************************/
2/*                                                                         */
3/*  psobjs.c                                                               */
4/*                                                                         */
5/*    Auxiliary functions for PostScript fonts (body).                     */
6/*                                                                         */
7/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 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_POSTSCRIPT_AUX_H
21#include FT_INTERNAL_DEBUG_H
22
23#include "psobjs.h"
24#include "psconv.h"
25
26#include "psauxerr.h"
27
28
29  /*************************************************************************/
30  /*                                                                       */
31  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
32  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
33  /* messages during execution.                                            */
34  /*                                                                       */
35#undef  FT_COMPONENT
36#define FT_COMPONENT  trace_psobjs
37
38
39  /*************************************************************************/
40  /*************************************************************************/
41  /*****                                                               *****/
42  /*****                             PS_TABLE                          *****/
43  /*****                                                               *****/
44  /*************************************************************************/
45  /*************************************************************************/
46
47  /*************************************************************************/
48  /*                                                                       */
49  /* <Function>                                                            */
50  /*    ps_table_new                                                       */
51  /*                                                                       */
52  /* <Description>                                                         */
53  /*    Initializes a PS_Table.                                            */
54  /*                                                                       */
55  /* <InOut>                                                               */
56  /*    table  :: The address of the target table.                         */
57  /*                                                                       */
58  /* <Input>                                                               */
59  /*    count  :: The table size = the maximum number of elements.         */
60  /*                                                                       */
61  /*    memory :: The memory object to use for all subsequent              */
62  /*              reallocations.                                           */
63  /*                                                                       */
64  /* <Return>                                                              */
65  /*    FreeType error code.  0 means success.                             */
66  /*                                                                       */
67  FT_LOCAL_DEF( FT_Error )
68  ps_table_new( PS_Table   table,
69                FT_Int     count,
70                FT_Memory  memory )
71  {
72    FT_Error  error;
73
74
75    table->memory = memory;
76    if ( FT_NEW_ARRAY( table->elements, count ) ||
77         FT_NEW_ARRAY( table->lengths,  count ) )
78      goto Exit;
79
80    table->max_elems = count;
81    table->init      = 0xDEADBEEFUL;
82    table->num_elems = 0;
83    table->block     = 0;
84    table->capacity  = 0;
85    table->cursor    = 0;
86
87    *(PS_Table_FuncsRec*)&table->funcs = ps_table_funcs;
88
89  Exit:
90    if ( error )
91      FT_FREE( table->elements );
92
93    return error;
94  }
95
96
97  static void
98  shift_elements( PS_Table  table,
99                  FT_Byte*  old_base )
100  {
101    FT_PtrDist  delta  = table->block - old_base;
102    FT_Byte**   offset = table->elements;
103    FT_Byte**   limit  = offset + table->max_elems;
104
105
106    for ( ; offset < limit; offset++ )
107    {
108      if ( offset[0] )
109        offset[0] += delta;
110    }
111  }
112
113
114  static FT_Error
115  reallocate_t1_table( PS_Table  table,
116                       FT_Long   new_size )
117  {
118    FT_Memory  memory   = table->memory;
119    FT_Byte*   old_base = table->block;
120    FT_Error   error;
121
122
123    /* allocate new base block */
124    if ( FT_ALLOC( table->block, new_size ) )
125    {
126      table->block = old_base;
127      return error;
128    }
129
130    /* copy elements and shift offsets */
131    if ( old_base )
132    {
133      FT_MEM_COPY( table->block, old_base, table->capacity );
134      shift_elements( table, old_base );
135      FT_FREE( old_base );
136    }
137
138    table->capacity = new_size;
139
140    return PSaux_Err_Ok;
141  }
142
143
144  /*************************************************************************/
145  /*                                                                       */
146  /* <Function>                                                            */
147  /*    ps_table_add                                                       */
148  /*                                                                       */
149  /* <Description>                                                         */
150  /*    Adds an object to a PS_Table, possibly growing its memory block.   */
151  /*                                                                       */
152  /* <InOut>                                                               */
153  /*    table  :: The target table.                                        */
154  /*                                                                       */
155  /* <Input>                                                               */
156  /*    idx    :: The index of the object in the table.                    */
157  /*                                                                       */
158  /*    object :: The address of the object to copy in memory.             */
159  /*                                                                       */
160  /*    length :: The length in bytes of the source object.                */
161  /*                                                                       */
162  /* <Return>                                                              */
163  /*    FreeType error code.  0 means success.  An error is returned if a  */
164  /*    reallocation fails.                                                */
165  /*                                                                       */
166  FT_LOCAL_DEF( FT_Error )
167  ps_table_add( PS_Table    table,
168                FT_Int      idx,
169                void*       object,
170                FT_PtrDist  length )
171  {
172    if ( idx < 0 || idx >= table->max_elems )
173    {
174      FT_ERROR(( "ps_table_add: invalid index\n" ));
175      return PSaux_Err_Invalid_Argument;
176    }
177
178    /* grow the base block if needed */
179    if ( table->cursor + length > table->capacity )
180    {
181      FT_Error   error;
182      FT_Offset  new_size  = table->capacity;
183      FT_Long    in_offset;
184
185
186      in_offset = (FT_Long)((FT_Byte*)object - table->block);
187      if ( (FT_ULong)in_offset >= table->capacity )
188        in_offset = -1;
189
190      while ( new_size < table->cursor + length )
191      {
192        /* increase size by 25% and round up to the nearest multiple
193           of 1024 */
194        new_size += ( new_size >> 2 ) + 1;
195        new_size  = FT_PAD_CEIL( new_size, 1024 );
196      }
197
198      error = reallocate_t1_table( table, new_size );
199      if ( error )
200        return error;
201
202      if ( in_offset >= 0 )
203        object = table->block + in_offset;
204    }
205
206    /* add the object to the base block and adjust offset */
207    table->elements[idx] = table->block + table->cursor;
208    table->lengths [idx] = length;
209    FT_MEM_COPY( table->block + table->cursor, object, length );
210
211    table->cursor += length;
212    return PSaux_Err_Ok;
213  }
214
215
216  /*************************************************************************/
217  /*                                                                       */
218  /* <Function>                                                            */
219  /*    ps_table_done                                                      */
220  /*                                                                       */
221  /* <Description>                                                         */
222  /*    Finalizes a PS_TableRec (i.e., reallocate it to its current        */
223  /*    cursor).                                                           */
224  /*                                                                       */
225  /* <InOut>                                                               */
226  /*    table :: The target table.                                         */
227  /*                                                                       */
228  /* <Note>                                                                */
229  /*    This function does NOT release the heap's memory block.  It is up  */
230  /*    to the caller to clean it, or reference it in its own structures.  */
231  /*                                                                       */
232  FT_LOCAL_DEF( void )
233  ps_table_done( PS_Table  table )
234  {
235    FT_Memory  memory = table->memory;
236    FT_Error   error;
237    FT_Byte*   old_base = table->block;
238
239
240    /* should never fail, because rec.cursor <= rec.size */
241    if ( !old_base )
242      return;
243
244    if ( FT_ALLOC( table->block, table->cursor ) )
245      return;
246    FT_MEM_COPY( table->block, old_base, table->cursor );
247    shift_elements( table, old_base );
248
249    table->capacity = table->cursor;
250    FT_FREE( old_base );
251
252    FT_UNUSED( error );
253  }
254
255
256  FT_LOCAL_DEF( void )
257  ps_table_release( PS_Table  table )
258  {
259    FT_Memory  memory = table->memory;
260
261
262    if ( (FT_ULong)table->init == 0xDEADBEEFUL )
263    {
264      FT_FREE( table->block );
265      FT_FREE( table->elements );
266      FT_FREE( table->lengths );
267      table->init = 0;
268    }
269  }
270
271
272  /*************************************************************************/
273  /*************************************************************************/
274  /*****                                                               *****/
275  /*****                            T1 PARSER                          *****/
276  /*****                                                               *****/
277  /*************************************************************************/
278  /*************************************************************************/
279
280
281  /* first character must be already part of the comment */
282
283  static void
284  skip_comment( FT_Byte*  *acur,
285                FT_Byte*   limit )
286  {
287    FT_Byte*  cur = *acur;
288
289
290    while ( cur < limit )
291    {
292      if ( IS_PS_NEWLINE( *cur ) )
293        break;
294      cur++;
295    }
296
297    *acur = cur;
298  }
299
300
301  static void
302  skip_spaces( FT_Byte*  *acur,
303               FT_Byte*   limit )
304  {
305    FT_Byte*  cur = *acur;
306
307
308    while ( cur < limit )
309    {
310      if ( !IS_PS_SPACE( *cur ) )
311      {
312        if ( *cur == '%' )
313          /* According to the PLRM, a comment is equal to a space. */
314          skip_comment( &cur, limit );
315        else
316          break;
317      }
318      cur++;
319    }
320
321    *acur = cur;
322  }
323
324
325#define IS_OCTAL_DIGIT( c ) ( '0' <= (c) && (c) <= '7' )
326
327
328  /* first character must be `(';                               */
329  /* *acur is positioned at the character after the closing `)' */
330
331  static FT_Error
332  skip_literal_string( FT_Byte*  *acur,
333                       FT_Byte*   limit )
334  {
335    FT_Byte*      cur   = *acur;
336    FT_Int        embed = 0;
337    FT_Error      error = PSaux_Err_Invalid_File_Format;
338    unsigned int  i;
339
340
341    while ( cur < limit )
342    {
343      FT_Byte  c = *cur;
344
345
346      ++cur;
347
348      if ( c == '\\' )
349      {
350        /* Red Book 3rd ed., section `Literal Text Strings', p. 29:     */
351        /* A backslash can introduce three different types              */
352        /* of escape sequences:                                         */
353        /*   - a special escaped char like \r, \n, etc.                 */
354        /*   - a one-, two-, or three-digit octal number                */
355        /*   - none of the above in which case the backslash is ignored */
356
357        if ( cur == limit )
358          /* error (or to be ignored?) */
359          break;
360
361        switch ( *cur )
362        {
363          /* skip `special' escape */
364        case 'n':
365        case 'r':
366        case 't':
367        case 'b':
368        case 'f':
369        case '\\':
370        case '(':
371        case ')':
372          ++cur;
373          break;
374
375        default:
376          /* skip octal escape or ignore backslash */
377          for ( i = 0; i < 3 && cur < limit; ++i )
378          {
379            if ( ! IS_OCTAL_DIGIT( *cur ) )
380              break;
381
382            ++cur;
383          }
384        }
385      }
386      else if ( c == '(' )
387        embed++;
388      else if ( c == ')' )
389      {
390        embed--;
391        if ( embed == 0 )
392        {
393          error = PSaux_Err_Ok;
394          break;
395        }
396      }
397    }
398
399    *acur = cur;
400
401    return error;
402  }
403
404
405  /* first character must be `<' */
406
407  static FT_Error
408  skip_string( FT_Byte*  *acur,
409               FT_Byte*   limit )
410  {
411    FT_Byte*  cur = *acur;
412    FT_Error  err =  PSaux_Err_Ok;
413
414
415    while ( ++cur < limit )
416    {
417      /* All whitespace characters are ignored. */
418      skip_spaces( &cur, limit );
419      if ( cur >= limit )
420        break;
421
422      if ( !IS_PS_XDIGIT( *cur ) )
423        break;
424    }
425
426    if ( cur < limit && *cur != '>' )
427    {
428      FT_ERROR(( "skip_string: missing closing delimiter `>'\n" ));
429      err = PSaux_Err_Invalid_File_Format;
430    }
431    else
432      cur++;
433
434    *acur = cur;
435    return err;
436  }
437
438
439  /* first character must be the opening brace that */
440  /* starts the procedure                           */
441
442  /* NB: [ and ] need not match:                    */
443  /* `/foo {[} def' is a valid PostScript fragment, */
444  /* even within a Type1 font                       */
445
446  static FT_Error
447  skip_procedure( FT_Byte*  *acur,
448                  FT_Byte*   limit )
449  {
450    FT_Byte*  cur;
451    FT_Int    embed = 0;
452    FT_Error  error = PSaux_Err_Ok;
453
454
455    FT_ASSERT( **acur == '{' );
456
457    for ( cur = *acur; cur < limit && error == PSaux_Err_Ok; ++cur )
458    {
459      switch ( *cur )
460      {
461      case '{':
462        ++embed;
463        break;
464
465      case '}':
466        --embed;
467        if ( embed == 0 )
468        {
469          ++cur;
470          goto end;
471        }
472        break;
473
474      case '(':
475        error = skip_literal_string( &cur, limit );
476        break;
477
478      case '<':
479        error = skip_string( &cur, limit );
480        break;
481
482      case '%':
483        skip_comment( &cur, limit );
484        break;
485      }
486    }
487
488  end:
489    if ( embed != 0 )
490      error = PSaux_Err_Invalid_File_Format;
491
492    *acur = cur;
493
494    return error;
495  }
496
497
498  /***********************************************************************/
499  /*                                                                     */
500  /* All exported parsing routines handle leading whitespace and stop at */
501  /* the first character which isn't part of the just handled token.     */
502  /*                                                                     */
503  /***********************************************************************/
504
505
506  FT_LOCAL_DEF( void )
507  ps_parser_skip_PS_token( PS_Parser  parser )
508  {
509    /* Note: PostScript allows any non-delimiting, non-whitespace        */
510    /*       character in a name (PS Ref Manual, 3rd ed, p31).           */
511    /*       PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */
512
513    FT_Byte*  cur   = parser->cursor;
514    FT_Byte*  limit = parser->limit;
515    FT_Error  error = PSaux_Err_Ok;
516
517
518    skip_spaces( &cur, limit );             /* this also skips comments */
519    if ( cur >= limit )
520      goto Exit;
521
522    /* self-delimiting, single-character tokens */
523    if ( *cur == '[' || *cur == ']' )
524    {
525      cur++;
526      goto Exit;
527    }
528
529    /* skip balanced expressions (procedures and strings) */
530
531    if ( *cur == '{' )                              /* {...} */
532    {
533      error = skip_procedure( &cur, limit );
534      goto Exit;
535    }
536
537    if ( *cur == '(' )                              /* (...) */
538    {
539      error = skip_literal_string( &cur, limit );
540      goto Exit;
541    }
542
543    if ( *cur == '<' )                              /* <...> */
544    {
545      if ( cur + 1 < limit && *(cur + 1) == '<' )   /* << */
546      {
547        cur++;
548        cur++;
549      }
550      else
551        error = skip_string( &cur, limit );
552
553      goto Exit;
554    }
555
556    if ( *cur == '>' )
557    {
558      cur++;
559      if ( cur >= limit || *cur != '>' )             /* >> */
560      {
561        FT_ERROR(( "ps_parser_skip_PS_token: "
562                   "unexpected closing delimiter `>'\n" ));
563        error = PSaux_Err_Invalid_File_Format;
564        goto Exit;
565      }
566      cur++;
567      goto Exit;
568    }
569
570    if ( *cur == '/' )
571      cur++;
572
573    /* anything else */
574    while ( cur < limit )
575    {
576      /* *cur might be invalid (e.g., ')' or '}'), but this   */
577      /* is handled by the test `cur == parser->cursor' below */
578      if ( IS_PS_DELIM( *cur ) )
579        break;
580
581      cur++;
582    }
583
584  Exit:
585    if ( cur == parser->cursor )
586    {
587      FT_ERROR(( "ps_parser_skip_PS_token: "
588                 "current token is `%c', which is self-delimiting "
589                 "but invalid at this point\n",
590                 *cur ));
591
592      error = PSaux_Err_Invalid_File_Format;
593    }
594
595    parser->error  = error;
596    parser->cursor = cur;
597  }
598
599
600  FT_LOCAL_DEF( void )
601  ps_parser_skip_spaces( PS_Parser  parser )
602  {
603    skip_spaces( &parser->cursor, parser->limit );
604  }
605
606
607  /* `token' here means either something between balanced delimiters */
608  /* or the next token; the delimiters are not removed.              */
609
610  FT_LOCAL_DEF( void )
611  ps_parser_to_token( PS_Parser  parser,
612                      T1_Token   token )
613  {
614    FT_Byte*  cur;
615    FT_Byte*  limit;
616    FT_Int    embed;
617
618
619    token->type  = T1_TOKEN_TYPE_NONE;
620    token->start = 0;
621    token->limit = 0;
622
623    /* first of all, skip leading whitespace */
624    ps_parser_skip_spaces( parser );
625
626    cur   = parser->cursor;
627    limit = parser->limit;
628
629    if ( cur >= limit )
630      return;
631
632    switch ( *cur )
633    {
634      /************* check for literal string *****************/
635    case '(':
636      token->type  = T1_TOKEN_TYPE_STRING;
637      token->start = cur;
638
639      if ( skip_literal_string( &cur, limit ) == PSaux_Err_Ok )
640        token->limit = cur;
641      break;
642
643      /************* check for programs/array *****************/
644    case '{':
645      token->type  = T1_TOKEN_TYPE_ARRAY;
646      token->start = cur;
647
648      if ( skip_procedure( &cur, limit ) == PSaux_Err_Ok )
649        token->limit = cur;
650      break;
651
652      /************* check for table/array ********************/
653      /* XXX: in theory we should also look for "<<"          */
654      /*      since this is semantically equivalent to "[";   */
655      /*      in practice it doesn't matter (?)               */
656    case '[':
657      token->type  = T1_TOKEN_TYPE_ARRAY;
658      embed        = 1;
659      token->start = cur++;
660
661      /* we need this to catch `[ ]' */
662      parser->cursor = cur;
663      ps_parser_skip_spaces( parser );
664      cur = parser->cursor;
665
666      while ( cur < limit && !parser->error )
667      {
668        /* XXX: this is wrong because it does not      */
669        /*      skip comments, procedures, and strings */
670        if ( *cur == '[' )
671          embed++;
672        else if ( *cur == ']' )
673        {
674          embed--;
675          if ( embed <= 0 )
676          {
677            token->limit = ++cur;
678            break;
679          }
680        }
681
682        parser->cursor = cur;
683        ps_parser_skip_PS_token( parser );
684        /* we need this to catch `[XXX ]' */
685        ps_parser_skip_spaces  ( parser );
686        cur = parser->cursor;
687      }
688      break;
689
690      /* ************ otherwise, it is any token **************/
691    default:
692      token->start = cur;
693      token->type  = ( *cur == '/' ? T1_TOKEN_TYPE_KEY : T1_TOKEN_TYPE_ANY );
694      ps_parser_skip_PS_token( parser );
695      cur = parser->cursor;
696      if ( !parser->error )
697        token->limit = cur;
698    }
699
700    if ( !token->limit )
701    {
702      token->start = 0;
703      token->type  = T1_TOKEN_TYPE_NONE;
704    }
705
706    parser->cursor = cur;
707  }
708
709
710  /* NB: `tokens' can be NULL if we only want to count */
711  /* the number of array elements                      */
712
713  FT_LOCAL_DEF( void )
714  ps_parser_to_token_array( PS_Parser  parser,
715                            T1_Token   tokens,
716                            FT_UInt    max_tokens,
717                            FT_Int*    pnum_tokens )
718  {
719    T1_TokenRec  master;
720
721
722    *pnum_tokens = -1;
723
724    /* this also handles leading whitespace */
725    ps_parser_to_token( parser, &master );
726
727    if ( master.type == T1_TOKEN_TYPE_ARRAY )
728    {
729      FT_Byte*  old_cursor = parser->cursor;
730      FT_Byte*  old_limit  = parser->limit;
731      T1_Token  cur        = tokens;
732      T1_Token  limit      = cur + max_tokens;
733
734
735      /* don't include outermost delimiters */
736      parser->cursor = master.start + 1;
737      parser->limit  = master.limit - 1;
738
739      while ( parser->cursor < parser->limit )
740      {
741        T1_TokenRec  token;
742
743
744        ps_parser_to_token( parser, &token );
745        if ( !token.type )
746          break;
747
748        if ( tokens != NULL && cur < limit )
749          *cur = token;
750
751        cur++;
752      }
753
754      *pnum_tokens = (FT_Int)( cur - tokens );
755
756      parser->cursor = old_cursor;
757      parser->limit  = old_limit;
758    }
759  }
760
761
762  /* first character must be a delimiter or a part of a number */
763  /* NB: `coords' can be NULL if we just want to skip the      */
764  /*     array; in this case we ignore `max_coords'            */
765
766  static FT_Int
767  ps_tocoordarray( FT_Byte*  *acur,
768                   FT_Byte*   limit,
769                   FT_Int     max_coords,
770                   FT_Short*  coords )
771  {
772    FT_Byte*  cur   = *acur;
773    FT_Int    count = 0;
774    FT_Byte   c, ender;
775
776
777    if ( cur >= limit )
778      goto Exit;
779
780    /* check for the beginning of an array; otherwise, only one number */
781    /* will be read                                                    */
782    c     = *cur;
783    ender = 0;
784
785    if ( c == '[' )
786      ender = ']';
787    else if ( c == '{' )
788      ender = '}';
789
790    if ( ender )
791      cur++;
792
793    /* now, read the coordinates */
794    while ( cur < limit )
795    {
796      FT_Short  dummy;
797      FT_Byte*  old_cur;
798
799
800      /* skip whitespace in front of data */
801      skip_spaces( &cur, limit );
802      if ( cur >= limit )
803        goto Exit;
804
805      if ( *cur == ender )
806      {
807        cur++;
808        break;
809      }
810
811      old_cur = cur;
812
813      if ( coords != NULL && count >= max_coords )
814        break;
815
816      /* call PS_Conv_ToFixed() even if coords == NULL */
817      /* to properly parse number at `cur'             */
818      *( coords != NULL ? &coords[count] : &dummy ) =
819        (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 );
820
821      if ( old_cur == cur )
822      {
823        count = -1;
824        goto Exit;
825      }
826      else
827        count++;
828
829      if ( !ender )
830        break;
831    }
832
833  Exit:
834    *acur = cur;
835    return count;
836  }
837
838
839  /* first character must be a delimiter or a part of a number */
840  /* NB: `values' can be NULL if we just want to skip the      */
841  /*     array; in this case we ignore `max_values'            */
842
843  static FT_Int
844  ps_tofixedarray( FT_Byte*  *acur,
845                   FT_Byte*   limit,
846                   FT_Int     max_values,
847                   FT_Fixed*  values,
848                   FT_Int     power_ten )
849  {
850    FT_Byte*  cur   = *acur;
851    FT_Int    count = 0;
852    FT_Byte   c, ender;
853
854
855    if ( cur >= limit )
856      goto Exit;
857
858    /* Check for the beginning of an array.  Otherwise, only one number */
859    /* will be read.                                                    */
860    c     = *cur;
861    ender = 0;
862
863    if ( c == '[' )
864      ender = ']';
865    else if ( c == '{' )
866      ender = '}';
867
868    if ( ender )
869      cur++;
870
871    /* now, read the values */
872    while ( cur < limit )
873    {
874      FT_Fixed  dummy;
875      FT_Byte*  old_cur;
876
877
878      /* skip whitespace in front of data */
879      skip_spaces( &cur, limit );
880      if ( cur >= limit )
881        goto Exit;
882
883      if ( *cur == ender )
884      {
885        cur++;
886        break;
887      }
888
889      old_cur = cur;
890
891      if ( values != NULL && count >= max_values )
892        break;
893
894      /* call PS_Conv_ToFixed() even if coords == NULL */
895      /* to properly parse number at `cur'             */
896      *( values != NULL ? &values[count] : &dummy ) =
897        PS_Conv_ToFixed( &cur, limit, power_ten );
898
899      if ( old_cur == cur )
900      {
901        count = -1;
902        goto Exit;
903      }
904      else
905        count++;
906
907      if ( !ender )
908        break;
909    }
910
911  Exit:
912    *acur = cur;
913    return count;
914  }
915
916
917#if 0
918
919  static FT_String*
920  ps_tostring( FT_Byte**  cursor,
921               FT_Byte*   limit,
922               FT_Memory  memory )
923  {
924    FT_Byte*    cur = *cursor;
925    FT_PtrDist  len = 0;
926    FT_Int      count;
927    FT_String*  result;
928    FT_Error    error;
929
930
931    /* XXX: some stupid fonts have a `Notice' or `Copyright' string     */
932    /*      that simply doesn't begin with an opening parenthesis, even */
933    /*      though they have a closing one!  E.g. "amuncial.pfb"        */
934    /*                                                                  */
935    /*      We must deal with these ill-fated cases there.  Note that   */
936    /*      these fonts didn't work with the old Type 1 driver as the   */
937    /*      notice/copyright was not recognized as a valid string token */
938    /*      and made the old token parser commit errors.                */
939
940    while ( cur < limit && ( *cur == ' ' || *cur == '\t' ) )
941      cur++;
942    if ( cur + 1 >= limit )
943      return 0;
944
945    if ( *cur == '(' )
946      cur++;  /* skip the opening parenthesis, if there is one */
947
948    *cursor = cur;
949    count   = 0;
950
951    /* then, count its length */
952    for ( ; cur < limit; cur++ )
953    {
954      if ( *cur == '(' )
955        count++;
956
957      else if ( *cur == ')' )
958      {
959        count--;
960        if ( count < 0 )
961          break;
962      }
963    }
964
965    len = cur - *cursor;
966    if ( cur >= limit || FT_ALLOC( result, len + 1 ) )
967      return 0;
968
969    /* now copy the string */
970    FT_MEM_COPY( result, *cursor, len );
971    result[len] = '\0';
972    *cursor = cur;
973    return result;
974  }
975
976#endif /* 0 */
977
978
979  static int
980  ps_tobool( FT_Byte*  *acur,
981             FT_Byte*   limit )
982  {
983    FT_Byte*  cur    = *acur;
984    FT_Bool   result = 0;
985
986
987    /* return 1 if we find `true', 0 otherwise */
988    if ( cur + 3 < limit &&
989         cur[0] == 't'   &&
990         cur[1] == 'r'   &&
991         cur[2] == 'u'   &&
992         cur[3] == 'e'   )
993    {
994      result = 1;
995      cur   += 5;
996    }
997    else if ( cur + 4 < limit &&
998              cur[0] == 'f'   &&
999              cur[1] == 'a'   &&
1000              cur[2] == 'l'   &&
1001              cur[3] == 's'   &&
1002              cur[4] == 'e'   )
1003    {
1004      result = 0;
1005      cur   += 6;
1006    }
1007
1008    *acur = cur;
1009    return result;
1010  }
1011
1012
1013  /* load a simple field (i.e. non-table) into the current list of objects */
1014
1015  FT_LOCAL_DEF( FT_Error )
1016  ps_parser_load_field( PS_Parser       parser,
1017                        const T1_Field  field,
1018                        void**          objects,
1019                        FT_UInt         max_objects,
1020                        FT_ULong*       pflags )
1021  {
1022    T1_TokenRec  token;
1023    FT_Byte*     cur;
1024    FT_Byte*     limit;
1025    FT_UInt      count;
1026    FT_UInt      idx;
1027    FT_Error     error;
1028
1029
1030    /* this also skips leading whitespace */
1031    ps_parser_to_token( parser, &token );
1032    if ( !token.type )
1033      goto Fail;
1034
1035    count = 1;
1036    idx   = 0;
1037    cur   = token.start;
1038    limit = token.limit;
1039
1040    /* we must detect arrays in /FontBBox */
1041    if ( field->type == T1_FIELD_TYPE_BBOX )
1042    {
1043      T1_TokenRec  token2;
1044      FT_Byte*     old_cur   = parser->cursor;
1045      FT_Byte*     old_limit = parser->limit;
1046
1047
1048      /* don't include delimiters */
1049      parser->cursor = token.start + 1;
1050      parser->limit  = token.limit - 1;
1051
1052      ps_parser_to_token( parser, &token2 );
1053      parser->cursor = old_cur;
1054      parser->limit  = old_limit;
1055
1056      if ( token2.type == T1_TOKEN_TYPE_ARRAY )
1057        goto FieldArray;
1058    }
1059    else if ( token.type == T1_TOKEN_TYPE_ARRAY )
1060    {
1061    FieldArray:
1062      /* if this is an array and we have no blend, an error occurs */
1063      if ( max_objects == 0 )
1064        goto Fail;
1065
1066      count = max_objects;
1067      idx   = 1;
1068
1069      /* don't include delimiters */
1070      cur++;
1071      limit--;
1072    }
1073
1074    for ( ; count > 0; count--, idx++ )
1075    {
1076      FT_Byte*    q = (FT_Byte*)objects[idx] + field->offset;
1077      FT_Long     val;
1078      FT_String*  string;
1079
1080
1081      skip_spaces( &cur, limit );
1082
1083      switch ( field->type )
1084      {
1085      case T1_FIELD_TYPE_BOOL:
1086        val = ps_tobool( &cur, limit );
1087        goto Store_Integer;
1088
1089      case T1_FIELD_TYPE_FIXED:
1090        val = PS_Conv_ToFixed( &cur, limit, 0 );
1091        goto Store_Integer;
1092
1093      case T1_FIELD_TYPE_FIXED_1000:
1094        val = PS_Conv_ToFixed( &cur, limit, 3 );
1095        goto Store_Integer;
1096
1097      case T1_FIELD_TYPE_INTEGER:
1098        val = PS_Conv_ToInt( &cur, limit );
1099        /* fall through */
1100
1101      Store_Integer:
1102        switch ( field->size )
1103        {
1104        case (8 / FT_CHAR_BIT):
1105          *(FT_Byte*)q = (FT_Byte)val;
1106          break;
1107
1108        case (16 / FT_CHAR_BIT):
1109          *(FT_UShort*)q = (FT_UShort)val;
1110          break;
1111
1112        case (32 / FT_CHAR_BIT):
1113          *(FT_UInt32*)q = (FT_UInt32)val;
1114          break;
1115
1116        default:                /* for 64-bit systems */
1117          *(FT_Long*)q = val;
1118        }
1119        break;
1120
1121      case T1_FIELD_TYPE_STRING:
1122      case T1_FIELD_TYPE_KEY:
1123        {
1124          FT_Memory  memory = parser->memory;
1125          FT_UInt    len    = (FT_UInt)( limit - cur );
1126
1127
1128          if ( cur >= limit )
1129            break;
1130
1131          /* we allow both a string or a name   */
1132          /* for cases like /FontName (foo) def */
1133          if ( token.type == T1_TOKEN_TYPE_KEY )
1134          {
1135            /* don't include leading `/' */
1136            len--;
1137            cur++;
1138          }
1139          else if ( token.type == T1_TOKEN_TYPE_STRING )
1140          {
1141            /* don't include delimiting parentheses    */
1142            /* XXX we don't handle <<...>> here        */
1143            /* XXX should we convert octal escapes?    */
1144            /*     if so, what encoding should we use? */
1145            cur++;
1146            len -= 2;
1147          }
1148          else
1149          {
1150            FT_ERROR(( "ps_parser_load_field: expected a name or string "
1151                       "but found token of type %d instead\n",
1152                       token.type ));
1153            error = PSaux_Err_Invalid_File_Format;
1154            goto Exit;
1155          }
1156
1157          /* for this to work (FT_String**)q must have been */
1158          /* initialized to NULL                            */
1159          if ( *(FT_String**)q != NULL )
1160          {
1161            FT_TRACE0(( "ps_parser_load_field: overwriting field %s\n",
1162                        field->ident ));
1163            FT_FREE( *(FT_String**)q );
1164            *(FT_String**)q = NULL;
1165          }
1166
1167          if ( FT_ALLOC( string, len + 1 ) )
1168            goto Exit;
1169
1170          FT_MEM_COPY( string, cur, len );
1171          string[len] = 0;
1172
1173          *(FT_String**)q = string;
1174        }
1175        break;
1176
1177      case T1_FIELD_TYPE_BBOX:
1178        {
1179          FT_Fixed  temp[4];
1180          FT_BBox*  bbox = (FT_BBox*)q;
1181          FT_Int    result;
1182
1183
1184          result = ps_tofixedarray( &cur, limit, 4, temp, 0 );
1185
1186          if ( result < 0 )
1187          {
1188            FT_ERROR(( "ps_parser_load_field: "
1189                       "expected four integers in bounding box\n" ));
1190            error = PSaux_Err_Invalid_File_Format;
1191            goto Exit;
1192          }
1193
1194          bbox->xMin = FT_RoundFix( temp[0] );
1195          bbox->yMin = FT_RoundFix( temp[1] );
1196          bbox->xMax = FT_RoundFix( temp[2] );
1197          bbox->yMax = FT_RoundFix( temp[3] );
1198        }
1199        break;
1200
1201      default:
1202        /* an error occurred */
1203        goto Fail;
1204      }
1205    }
1206
1207#if 0  /* obsolete -- keep for reference */
1208    if ( pflags )
1209      *pflags |= 1L << field->flag_bit;
1210#else
1211    FT_UNUSED( pflags );
1212#endif
1213
1214    error = PSaux_Err_Ok;
1215
1216  Exit:
1217    return error;
1218
1219  Fail:
1220    error = PSaux_Err_Invalid_File_Format;
1221    goto Exit;
1222  }
1223
1224
1225#define T1_MAX_TABLE_ELEMENTS  32
1226
1227
1228  FT_LOCAL_DEF( FT_Error )
1229  ps_parser_load_field_table( PS_Parser       parser,
1230                              const T1_Field  field,
1231                              void**          objects,
1232                              FT_UInt         max_objects,
1233                              FT_ULong*       pflags )
1234  {
1235    T1_TokenRec  elements[T1_MAX_TABLE_ELEMENTS];
1236    T1_Token     token;
1237    FT_Int       num_elements;
1238    FT_Error     error = PSaux_Err_Ok;
1239    FT_Byte*     old_cursor;
1240    FT_Byte*     old_limit;
1241    T1_FieldRec  fieldrec = *(T1_Field)field;
1242
1243
1244    fieldrec.type = T1_FIELD_TYPE_INTEGER;
1245    if ( field->type == T1_FIELD_TYPE_FIXED_ARRAY ||
1246         field->type == T1_FIELD_TYPE_BBOX        )
1247      fieldrec.type = T1_FIELD_TYPE_FIXED;
1248
1249    ps_parser_to_token_array( parser, elements,
1250                              T1_MAX_TABLE_ELEMENTS, &num_elements );
1251    if ( num_elements < 0 )
1252    {
1253      error = PSaux_Err_Ignore;
1254      goto Exit;
1255    }
1256    if ( (FT_UInt)num_elements > field->array_max )
1257      num_elements = field->array_max;
1258
1259    old_cursor = parser->cursor;
1260    old_limit  = parser->limit;
1261
1262    /* we store the elements count if necessary */
1263    if ( field->type != T1_FIELD_TYPE_BBOX )
1264      *(FT_Byte*)( (FT_Byte*)objects[0] + field->count_offset ) =
1265        (FT_Byte)num_elements;
1266
1267    /* we now load each element, adjusting the field.offset on each one */
1268    token = elements;
1269    for ( ; num_elements > 0; num_elements--, token++ )
1270    {
1271      parser->cursor = token->start;
1272      parser->limit  = token->limit;
1273      ps_parser_load_field( parser, &fieldrec, objects, max_objects, 0 );
1274      fieldrec.offset += fieldrec.size;
1275    }
1276
1277#if 0  /* obsolete -- keep for reference */
1278    if ( pflags )
1279      *pflags |= 1L << field->flag_bit;
1280#else
1281    FT_UNUSED( pflags );
1282#endif
1283
1284    parser->cursor = old_cursor;
1285    parser->limit  = old_limit;
1286
1287  Exit:
1288    return error;
1289  }
1290
1291
1292  FT_LOCAL_DEF( FT_Long )
1293  ps_parser_to_int( PS_Parser  parser )
1294  {
1295    ps_parser_skip_spaces( parser );
1296    return PS_Conv_ToInt( &parser->cursor, parser->limit );
1297  }
1298
1299
1300  /* first character must be `<' if `delimiters' is non-zero */
1301
1302  FT_LOCAL_DEF( FT_Error )
1303  ps_parser_to_bytes( PS_Parser  parser,
1304                      FT_Byte*   bytes,
1305                      FT_Long    max_bytes,
1306                      FT_Long*   pnum_bytes,
1307                      FT_Bool    delimiters )
1308  {
1309    FT_Error  error = PSaux_Err_Ok;
1310    FT_Byte*  cur;
1311
1312
1313    ps_parser_skip_spaces( parser );
1314    cur = parser->cursor;
1315
1316    if ( cur >= parser->limit )
1317      goto Exit;
1318
1319    if ( delimiters )
1320    {
1321      if ( *cur != '<' )
1322      {
1323        FT_ERROR(( "ps_parser_to_bytes: Missing starting delimiter `<'\n" ));
1324        error = PSaux_Err_Invalid_File_Format;
1325        goto Exit;
1326      }
1327
1328      cur++;
1329    }
1330
1331    *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur,
1332                                          parser->limit,
1333                                          bytes,
1334                                          max_bytes );
1335
1336    if ( delimiters )
1337    {
1338      if ( cur < parser->limit && *cur != '>' )
1339      {
1340        FT_ERROR(( "ps_parser_to_bytes: Missing closing delimiter `>'\n" ));
1341        error = PSaux_Err_Invalid_File_Format;
1342        goto Exit;
1343      }
1344
1345      cur++;
1346    }
1347
1348    parser->cursor = cur;
1349
1350  Exit:
1351    return error;
1352  }
1353
1354
1355  FT_LOCAL_DEF( FT_Fixed )
1356  ps_parser_to_fixed( PS_Parser  parser,
1357                      FT_Int     power_ten )
1358  {
1359    ps_parser_skip_spaces( parser );
1360    return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten );
1361  }
1362
1363
1364  FT_LOCAL_DEF( FT_Int )
1365  ps_parser_to_coord_array( PS_Parser  parser,
1366                            FT_Int     max_coords,
1367                            FT_Short*  coords )
1368  {
1369    ps_parser_skip_spaces( parser );
1370    return ps_tocoordarray( &parser->cursor, parser->limit,
1371                            max_coords, coords );
1372  }
1373
1374
1375  FT_LOCAL_DEF( FT_Int )
1376  ps_parser_to_fixed_array( PS_Parser  parser,
1377                            FT_Int     max_values,
1378                            FT_Fixed*  values,
1379                            FT_Int     power_ten )
1380  {
1381    ps_parser_skip_spaces( parser );
1382    return ps_tofixedarray( &parser->cursor, parser->limit,
1383                            max_values, values, power_ten );
1384  }
1385
1386
1387#if 0
1388
1389  FT_LOCAL_DEF( FT_String* )
1390  T1_ToString( PS_Parser  parser )
1391  {
1392    return ps_tostring( &parser->cursor, parser->limit, parser->memory );
1393  }
1394
1395
1396  FT_LOCAL_DEF( FT_Bool )
1397  T1_ToBool( PS_Parser  parser )
1398  {
1399    return ps_tobool( &parser->cursor, parser->limit );
1400  }
1401
1402#endif /* 0 */
1403
1404
1405  FT_LOCAL_DEF( void )
1406  ps_parser_init( PS_Parser  parser,
1407                  FT_Byte*   base,
1408                  FT_Byte*   limit,
1409                  FT_Memory  memory )
1410  {
1411    parser->error  = PSaux_Err_Ok;
1412    parser->base   = base;
1413    parser->limit  = limit;
1414    parser->cursor = base;
1415    parser->memory = memory;
1416    parser->funcs  = ps_parser_funcs;
1417  }
1418
1419
1420  FT_LOCAL_DEF( void )
1421  ps_parser_done( PS_Parser  parser )
1422  {
1423    FT_UNUSED( parser );
1424  }
1425
1426
1427  /*************************************************************************/
1428  /*************************************************************************/
1429  /*****                                                               *****/
1430  /*****                            T1 BUILDER                         *****/
1431  /*****                                                               *****/
1432  /*************************************************************************/
1433  /*************************************************************************/
1434
1435  /*************************************************************************/
1436  /*                                                                       */
1437  /* <Function>                                                            */
1438  /*    t1_builder_init                                                    */
1439  /*                                                                       */
1440  /* <Description>                                                         */
1441  /*    Initializes a given glyph builder.                                 */
1442  /*                                                                       */
1443  /* <InOut>                                                               */
1444  /*    builder :: A pointer to the glyph builder to initialize.           */
1445  /*                                                                       */
1446  /* <Input>                                                               */
1447  /*    face    :: The current face object.                                */
1448  /*                                                                       */
1449  /*    size    :: The current size object.                                */
1450  /*                                                                       */
1451  /*    glyph   :: The current glyph object.                               */
1452  /*                                                                       */
1453  /*    hinting :: Whether hinting should be applied.                      */
1454  /*                                                                       */
1455  FT_LOCAL_DEF( void )
1456  t1_builder_init( T1_Builder    builder,
1457                   FT_Face       face,
1458                   FT_Size       size,
1459                   FT_GlyphSlot  glyph,
1460                   FT_Bool       hinting )
1461  {
1462    builder->parse_state = T1_Parse_Start;
1463    builder->load_points = 1;
1464
1465    builder->face   = face;
1466    builder->glyph  = glyph;
1467    builder->memory = face->memory;
1468
1469    if ( glyph )
1470    {
1471      FT_GlyphLoader  loader = glyph->internal->loader;
1472
1473
1474      builder->loader  = loader;
1475      builder->base    = &loader->base.outline;
1476      builder->current = &loader->current.outline;
1477      FT_GlyphLoader_Rewind( loader );
1478
1479      builder->hints_globals = size->internal;
1480      builder->hints_funcs   = 0;
1481
1482      if ( hinting )
1483        builder->hints_funcs = glyph->internal->glyph_hints;
1484    }
1485
1486    builder->pos_x = 0;
1487    builder->pos_y = 0;
1488
1489    builder->left_bearing.x = 0;
1490    builder->left_bearing.y = 0;
1491    builder->advance.x      = 0;
1492    builder->advance.y      = 0;
1493
1494    builder->funcs = t1_builder_funcs;
1495  }
1496
1497
1498  /*************************************************************************/
1499  /*                                                                       */
1500  /* <Function>                                                            */
1501  /*    t1_builder_done                                                    */
1502  /*                                                                       */
1503  /* <Description>                                                         */
1504  /*    Finalizes a given glyph builder.  Its contents can still be used   */
1505  /*    after the call, but the function saves important information       */
1506  /*    within the corresponding glyph slot.                               */
1507  /*                                                                       */
1508  /* <Input>                                                               */
1509  /*    builder :: A pointer to the glyph builder to finalize.             */
1510  /*                                                                       */
1511  FT_LOCAL_DEF( void )
1512  t1_builder_done( T1_Builder  builder )
1513  {
1514    FT_GlyphSlot  glyph = builder->glyph;
1515
1516
1517    if ( glyph )
1518      glyph->outline = *builder->base;
1519  }
1520
1521
1522  /* check that there is enough space for `count' more points */
1523  FT_LOCAL_DEF( FT_Error )
1524  t1_builder_check_points( T1_Builder  builder,
1525                           FT_Int      count )
1526  {
1527    return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
1528  }
1529
1530
1531  /* add a new point, do not check space */
1532  FT_LOCAL_DEF( void )
1533  t1_builder_add_point( T1_Builder  builder,
1534                        FT_Pos      x,
1535                        FT_Pos      y,
1536                        FT_Byte     flag )
1537  {
1538    FT_Outline*  outline = builder->current;
1539
1540
1541    if ( builder->load_points )
1542    {
1543      FT_Vector*  point   = outline->points + outline->n_points;
1544      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points;
1545
1546
1547      if ( builder->shift )
1548      {
1549        x >>= 16;
1550        y >>= 16;
1551      }
1552      point->x = x;
1553      point->y = y;
1554      *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
1555
1556      builder->last = *point;
1557    }
1558    outline->n_points++;
1559  }
1560
1561
1562  /* check space for a new on-curve point, then add it */
1563  FT_LOCAL_DEF( FT_Error )
1564  t1_builder_add_point1( T1_Builder  builder,
1565                         FT_Pos      x,
1566                         FT_Pos      y )
1567  {
1568    FT_Error  error;
1569
1570
1571    error = t1_builder_check_points( builder, 1 );
1572    if ( !error )
1573      t1_builder_add_point( builder, x, y, 1 );
1574
1575    return error;
1576  }
1577
1578
1579  /* check space for a new contour, then add it */
1580  FT_LOCAL_DEF( FT_Error )
1581  t1_builder_add_contour( T1_Builder  builder )
1582  {
1583    FT_Outline*  outline = builder->current;
1584    FT_Error     error;
1585
1586
1587    if ( !builder->load_points )
1588    {
1589      outline->n_contours++;
1590      return PSaux_Err_Ok;
1591    }
1592
1593    error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
1594    if ( !error )
1595    {
1596      if ( outline->n_contours > 0 )
1597        outline->contours[outline->n_contours - 1] =
1598          (short)( outline->n_points - 1 );
1599
1600      outline->n_contours++;
1601    }
1602
1603    return error;
1604  }
1605
1606
1607  /* if a path was begun, add its first on-curve point */
1608  FT_LOCAL_DEF( FT_Error )
1609  t1_builder_start_point( T1_Builder  builder,
1610                          FT_Pos      x,
1611                          FT_Pos      y )
1612  {
1613    FT_Error  error = PSaux_Err_Invalid_File_Format;
1614
1615
1616    /* test whether we are building a new contour */
1617
1618    if ( builder->parse_state == T1_Parse_Have_Path )
1619      error = PSaux_Err_Ok;
1620    else if ( builder->parse_state == T1_Parse_Have_Moveto )
1621    {
1622      builder->parse_state = T1_Parse_Have_Path;
1623      error = t1_builder_add_contour( builder );
1624      if ( !error )
1625        error = t1_builder_add_point1( builder, x, y );
1626    }
1627
1628    return error;
1629  }
1630
1631
1632  /* close the current contour */
1633  FT_LOCAL_DEF( void )
1634  t1_builder_close_contour( T1_Builder  builder )
1635  {
1636    FT_Outline*  outline = builder->current;
1637    FT_Int       first;
1638
1639
1640    if ( !outline )
1641      return;
1642
1643    first = outline->n_contours <= 1
1644            ? 0 : outline->contours[outline->n_contours - 2] + 1;
1645
1646    /* We must not include the last point in the path if it */
1647    /* is located on the first point.                       */
1648    if ( outline->n_points > 1 )
1649    {
1650      FT_Vector*  p1      = outline->points + first;
1651      FT_Vector*  p2      = outline->points + outline->n_points - 1;
1652      FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points - 1;
1653
1654
1655      /* `delete' last point only if it coincides with the first */
1656      /* point and it is not a control point (which can happen). */
1657      if ( p1->x == p2->x && p1->y == p2->y )
1658        if ( *control == FT_CURVE_TAG_ON )
1659          outline->n_points--;
1660    }
1661
1662    if ( outline->n_contours > 0 )
1663    {
1664      /* Don't add contours only consisting of one point, i.e., */
1665      /* check whether begin point and last point are the same. */
1666      if ( first == outline->n_points - 1 )
1667      {
1668        outline->n_contours--;
1669        outline->n_points--;
1670      }
1671      else
1672        outline->contours[outline->n_contours - 1] =
1673          (short)( outline->n_points - 1 );
1674    }
1675  }
1676
1677
1678  /*************************************************************************/
1679  /*************************************************************************/
1680  /*****                                                               *****/
1681  /*****                            OTHER                              *****/
1682  /*****                                                               *****/
1683  /*************************************************************************/
1684  /*************************************************************************/
1685
1686  FT_LOCAL_DEF( void )
1687  t1_decrypt( FT_Byte*   buffer,
1688              FT_Offset  length,
1689              FT_UShort  seed )
1690  {
1691    PS_Conv_EexecDecode( &buffer,
1692                         buffer + length,
1693                         buffer,
1694                         length,
1695                         &seed );
1696  }
1697
1698
1699/* END */
Note: See TracBrowser for help on using the repository browser.