Home | History | Annotate | Download | only in type42
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  t42objs.c                                                              */
      4 /*                                                                         */
      5 /*    Type 42 objects manager (body).                                      */
      6 /*                                                                         */
      7 /*  Copyright 2002-2018 by                                                 */
      8 /*  Roberto Alameda.                                                       */
      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 "t42objs.h"
     20 #include "t42parse.h"
     21 #include "t42error.h"
     22 #include FT_INTERNAL_DEBUG_H
     23 #include FT_LIST_H
     24 #include FT_TRUETYPE_IDS_H
     25 
     26 
     27 #undef  FT_COMPONENT
     28 #define FT_COMPONENT  trace_t42
     29 
     30 
     31   static FT_Error
     32   T42_Open_Face( T42_Face  face )
     33   {
     34     T42_LoaderRec  loader;
     35     T42_Parser     parser;
     36     T1_Font        type1 = &face->type1;
     37     FT_Memory      memory = face->root.memory;
     38     FT_Error       error;
     39 
     40     PSAux_Service  psaux  = (PSAux_Service)face->psaux;
     41 
     42 
     43     t42_loader_init( &loader, face );
     44 
     45     parser = &loader.parser;
     46 
     47     if ( FT_ALLOC( face->ttf_data, 12 ) )
     48       goto Exit;
     49 
     50     /* while parsing the font we always update `face->ttf_size' so that */
     51     /* even in case of buggy data (which might lead to premature end of */
     52     /* scanning without causing an error) the call to `FT_Open_Face' in */
     53     /* `T42_Face_Init' passes the correct size                          */
     54     face->ttf_size = 12;
     55 
     56     error = t42_parser_init( parser,
     57                              face->root.stream,
     58                              memory,
     59                              psaux);
     60     if ( error )
     61       goto Exit;
     62 
     63     error = t42_parse_dict( face, &loader,
     64                             parser->base_dict, parser->base_len );
     65     if ( error )
     66       goto Exit;
     67 
     68     if ( type1->font_type != 42 )
     69     {
     70       FT_ERROR(( "T42_Open_Face: cannot handle FontType %d\n",
     71                  type1->font_type ));
     72       error = FT_THROW( Unknown_File_Format );
     73       goto Exit;
     74     }
     75 
     76     /* now, propagate the charstrings and glyphnames tables */
     77     /* to the Type1 data                                    */
     78     type1->num_glyphs = loader.num_glyphs;
     79 
     80     if ( !loader.charstrings.init )
     81     {
     82       FT_ERROR(( "T42_Open_Face: no charstrings array in face\n" ));
     83       error = FT_THROW( Invalid_File_Format );
     84     }
     85 
     86     loader.charstrings.init  = 0;
     87     type1->charstrings_block = loader.charstrings.block;
     88     type1->charstrings       = loader.charstrings.elements;
     89     type1->charstrings_len   = loader.charstrings.lengths;
     90 
     91     /* we copy the glyph names `block' and `elements' fields; */
     92     /* the `lengths' field must be released later             */
     93     type1->glyph_names_block    = loader.glyph_names.block;
     94     type1->glyph_names          = (FT_String**)loader.glyph_names.elements;
     95     loader.glyph_names.block    = NULL;
     96     loader.glyph_names.elements = NULL;
     97 
     98     /* we must now build type1.encoding when we have a custom array */
     99     if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY )
    100     {
    101       FT_Int    charcode, idx, min_char, max_char;
    102       FT_Byte*  glyph_name;
    103 
    104 
    105       /* OK, we do the following: for each element in the encoding   */
    106       /* table, look up the index of the glyph having the same name  */
    107       /* as defined in the CharStrings array.                        */
    108       /* The index is then stored in type1.encoding.char_index, and  */
    109       /* the name in type1.encoding.char_name                        */
    110 
    111       min_char = 0;
    112       max_char = 0;
    113 
    114       charcode = 0;
    115       for ( ; charcode < loader.encoding_table.max_elems; charcode++ )
    116       {
    117         FT_Byte*  char_name;
    118 
    119 
    120         type1->encoding.char_index[charcode] = 0;
    121         type1->encoding.char_name [charcode] = (char *)".notdef";
    122 
    123         char_name = loader.encoding_table.elements[charcode];
    124         if ( char_name )
    125           for ( idx = 0; idx < type1->num_glyphs; idx++ )
    126           {
    127             glyph_name = (FT_Byte*)type1->glyph_names[idx];
    128             if ( ft_strcmp( (const char*)char_name,
    129                             (const char*)glyph_name ) == 0 )
    130             {
    131               type1->encoding.char_index[charcode] = (FT_UShort)idx;
    132               type1->encoding.char_name [charcode] = (char*)glyph_name;
    133 
    134               /* Change min/max encoded char only if glyph name is */
    135               /* not /.notdef                                      */
    136               if ( ft_strcmp( (const char*)".notdef",
    137                               (const char*)glyph_name ) != 0 )
    138               {
    139                 if ( charcode < min_char )
    140                   min_char = charcode;
    141                 if ( charcode >= max_char )
    142                   max_char = charcode + 1;
    143               }
    144               break;
    145             }
    146           }
    147       }
    148 
    149       type1->encoding.code_first = min_char;
    150       type1->encoding.code_last  = max_char;
    151       type1->encoding.num_chars  = loader.num_chars;
    152     }
    153 
    154   Exit:
    155     t42_loader_done( &loader );
    156     return error;
    157   }
    158 
    159 
    160   /***************** Driver Functions *************/
    161 
    162 
    163   FT_LOCAL_DEF( FT_Error )
    164   T42_Face_Init( FT_Stream      stream,
    165                  FT_Face        t42face,       /* T42_Face */
    166                  FT_Int         face_index,
    167                  FT_Int         num_params,
    168                  FT_Parameter*  params )
    169   {
    170     T42_Face            face  = (T42_Face)t42face;
    171     FT_Error            error;
    172     FT_Service_PsCMaps  psnames;
    173     PSAux_Service       psaux;
    174     FT_Face             root  = (FT_Face)&face->root;
    175     T1_Font             type1 = &face->type1;
    176     PS_FontInfo         info  = &type1->font_info;
    177 
    178     FT_UNUSED( num_params );
    179     FT_UNUSED( params );
    180     FT_UNUSED( stream );
    181 
    182 
    183     face->ttf_face       = NULL;
    184     face->root.num_faces = 1;
    185 
    186     FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
    187     face->psnames = psnames;
    188 
    189     face->psaux = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ),
    190                                            "psaux" );
    191     psaux = (PSAux_Service)face->psaux;
    192     if ( !psaux )
    193     {
    194       FT_ERROR(( "T42_Face_Init: cannot access `psaux' module\n" ));
    195       error = FT_THROW( Missing_Module );
    196       goto Exit;
    197     }
    198 
    199     FT_TRACE2(( "Type 42 driver\n" ));
    200 
    201     /* open the tokenizer, this will also check the font format */
    202     error = T42_Open_Face( face );
    203     if ( error )
    204       goto Exit;
    205 
    206     /* if we just wanted to check the format, leave successfully now */
    207     if ( face_index < 0 )
    208       goto Exit;
    209 
    210     /* check the face index */
    211     if ( ( face_index & 0xFFFF ) > 0 )
    212     {
    213       FT_ERROR(( "T42_Face_Init: invalid face index\n" ));
    214       error = FT_THROW( Invalid_Argument );
    215       goto Exit;
    216     }
    217 
    218     /* Now load the font program into the face object */
    219 
    220     /* Init the face object fields */
    221     /* Now set up root face fields */
    222 
    223     root->num_glyphs   = type1->num_glyphs;
    224     root->num_charmaps = 0;
    225     root->face_index   = 0;
    226 
    227     root->face_flags |= FT_FACE_FLAG_SCALABLE    |
    228                         FT_FACE_FLAG_HORIZONTAL  |
    229                         FT_FACE_FLAG_GLYPH_NAMES;
    230 
    231     if ( info->is_fixed_pitch )
    232       root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
    233 
    234 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
    235     root->face_flags |= FT_FACE_FLAG_HINTER;
    236 #endif
    237 
    238     /* XXX: TODO -- add kerning with .afm support */
    239 
    240     /* get style name -- be careful, some broken fonts only */
    241     /* have a `/FontName' dictionary entry!                 */
    242     root->family_name = info->family_name;
    243     /* assume "Regular" style if we don't know better */
    244     root->style_name = (char *)"Regular";
    245     if ( root->family_name )
    246     {
    247       char*  full   = info->full_name;
    248       char*  family = root->family_name;
    249 
    250 
    251       if ( full )
    252       {
    253         while ( *full )
    254         {
    255           if ( *full == *family )
    256           {
    257             family++;
    258             full++;
    259           }
    260           else
    261           {
    262             if ( *full == ' ' || *full == '-' )
    263               full++;
    264             else if ( *family == ' ' || *family == '-' )
    265               family++;
    266             else
    267             {
    268               if ( !*family )
    269                 root->style_name = full;
    270               break;
    271             }
    272           }
    273         }
    274       }
    275     }
    276     else
    277     {
    278       /* do we have a `/FontName'? */
    279       if ( type1->font_name )
    280         root->family_name = type1->font_name;
    281     }
    282 
    283     /* no embedded bitmap support */
    284     root->num_fixed_sizes = 0;
    285     root->available_sizes = NULL;
    286 
    287     /* Load the TTF font embedded in the T42 font */
    288     {
    289       FT_Open_Args  args;
    290 
    291 
    292       args.flags       = FT_OPEN_MEMORY | FT_OPEN_DRIVER;
    293       args.driver      = FT_Get_Module( FT_FACE_LIBRARY( face ),
    294                                         "truetype" );
    295       args.memory_base = face->ttf_data;
    296       args.memory_size = face->ttf_size;
    297 
    298       if ( num_params )
    299       {
    300         args.flags     |= FT_OPEN_PARAMS;
    301         args.num_params = num_params;
    302         args.params     = params;
    303       }
    304 
    305       error = FT_Open_Face( FT_FACE_LIBRARY( face ),
    306                             &args, 0, &face->ttf_face );
    307     }
    308 
    309     if ( error )
    310       goto Exit;
    311 
    312     FT_Done_Size( face->ttf_face->size );
    313 
    314     /* Ignore info in FontInfo dictionary and use the info from the  */
    315     /* loaded TTF font.  The PostScript interpreter also ignores it. */
    316     root->bbox         = face->ttf_face->bbox;
    317     root->units_per_EM = face->ttf_face->units_per_EM;
    318 
    319     root->ascender  = face->ttf_face->ascender;
    320     root->descender = face->ttf_face->descender;
    321     root->height    = face->ttf_face->height;
    322 
    323     root->max_advance_width  = face->ttf_face->max_advance_width;
    324     root->max_advance_height = face->ttf_face->max_advance_height;
    325 
    326     root->underline_position  = (FT_Short)info->underline_position;
    327     root->underline_thickness = (FT_Short)info->underline_thickness;
    328 
    329     /* compute style flags */
    330     root->style_flags = 0;
    331     if ( info->italic_angle )
    332       root->style_flags |= FT_STYLE_FLAG_ITALIC;
    333 
    334     if ( face->ttf_face->style_flags & FT_STYLE_FLAG_BOLD )
    335       root->style_flags |= FT_STYLE_FLAG_BOLD;
    336 
    337     if ( face->ttf_face->face_flags & FT_FACE_FLAG_VERTICAL )
    338       root->face_flags |= FT_FACE_FLAG_VERTICAL;
    339 
    340     {
    341       if ( psnames )
    342       {
    343         FT_CharMapRec    charmap;
    344         T1_CMap_Classes  cmap_classes = psaux->t1_cmap_classes;
    345         FT_CMap_Class    clazz;
    346 
    347 
    348         charmap.face = root;
    349 
    350         /* first of all, try to synthesize a Unicode charmap */
    351         charmap.platform_id = TT_PLATFORM_MICROSOFT;
    352         charmap.encoding_id = TT_MS_ID_UNICODE_CS;
    353         charmap.encoding    = FT_ENCODING_UNICODE;
    354 
    355         error = FT_CMap_New( cmap_classes->unicode, NULL, &charmap, NULL );
    356         if ( error                                      &&
    357              FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) )
    358           goto Exit;
    359         error = FT_Err_Ok;
    360 
    361         /* now, generate an Adobe Standard encoding when appropriate */
    362         charmap.platform_id = TT_PLATFORM_ADOBE;
    363         clazz               = NULL;
    364 
    365         switch ( type1->encoding_type )
    366         {
    367         case T1_ENCODING_TYPE_STANDARD:
    368           charmap.encoding    = FT_ENCODING_ADOBE_STANDARD;
    369           charmap.encoding_id = TT_ADOBE_ID_STANDARD;
    370           clazz               = cmap_classes->standard;
    371           break;
    372 
    373         case T1_ENCODING_TYPE_EXPERT:
    374           charmap.encoding    = FT_ENCODING_ADOBE_EXPERT;
    375           charmap.encoding_id = TT_ADOBE_ID_EXPERT;
    376           clazz               = cmap_classes->expert;
    377           break;
    378 
    379         case T1_ENCODING_TYPE_ARRAY:
    380           charmap.encoding    = FT_ENCODING_ADOBE_CUSTOM;
    381           charmap.encoding_id = TT_ADOBE_ID_CUSTOM;
    382           clazz               = cmap_classes->custom;
    383           break;
    384 
    385         case T1_ENCODING_TYPE_ISOLATIN1:
    386           charmap.encoding    = FT_ENCODING_ADOBE_LATIN_1;
    387           charmap.encoding_id = TT_ADOBE_ID_LATIN_1;
    388           clazz               = cmap_classes->unicode;
    389           break;
    390 
    391         default:
    392           ;
    393         }
    394 
    395         if ( clazz )
    396           error = FT_CMap_New( clazz, NULL, &charmap, NULL );
    397       }
    398     }
    399   Exit:
    400     return error;
    401   }
    402 
    403 
    404   FT_LOCAL_DEF( void )
    405   T42_Face_Done( FT_Face  t42face )
    406   {
    407     T42_Face     face = (T42_Face)t42face;
    408     T1_Font      type1;
    409     PS_FontInfo  info;
    410     FT_Memory    memory;
    411 
    412 
    413     if ( !face )
    414       return;
    415 
    416     type1  = &face->type1;
    417     info   = &type1->font_info;
    418     memory = face->root.memory;
    419 
    420     /* delete internal ttf face prior to freeing face->ttf_data */
    421     if ( face->ttf_face )
    422       FT_Done_Face( face->ttf_face );
    423 
    424     /* release font info strings */
    425     FT_FREE( info->version );
    426     FT_FREE( info->notice );
    427     FT_FREE( info->full_name );
    428     FT_FREE( info->family_name );
    429     FT_FREE( info->weight );
    430 
    431     /* release top dictionary */
    432     FT_FREE( type1->charstrings_len );
    433     FT_FREE( type1->charstrings );
    434     FT_FREE( type1->glyph_names );
    435 
    436     FT_FREE( type1->charstrings_block );
    437     FT_FREE( type1->glyph_names_block );
    438 
    439     FT_FREE( type1->encoding.char_index );
    440     FT_FREE( type1->encoding.char_name );
    441     FT_FREE( type1->font_name );
    442 
    443     FT_FREE( face->ttf_data );
    444 
    445 #if 0
    446     /* release afm data if present */
    447     if ( face->afm_data )
    448       T1_Done_AFM( memory, (T1_AFM*)face->afm_data );
    449 #endif
    450 
    451     /* release unicode map, if any */
    452     FT_FREE( face->unicode_map.maps );
    453     face->unicode_map.num_maps = 0;
    454 
    455     face->root.family_name = NULL;
    456     face->root.style_name  = NULL;
    457   }
    458 
    459 
    460   /*************************************************************************/
    461   /*                                                                       */
    462   /* <Function>                                                            */
    463   /*    T42_Driver_Init                                                    */
    464   /*                                                                       */
    465   /* <Description>                                                         */
    466   /*    Initializes a given Type 42 driver object.                         */
    467   /*                                                                       */
    468   /* <Input>                                                               */
    469   /*    driver :: A handle to the target driver object.                    */
    470   /*                                                                       */
    471   /* <Return>                                                              */
    472   /*    FreeType error code.  0 means success.                             */
    473   /*                                                                       */
    474   FT_LOCAL_DEF( FT_Error )
    475   T42_Driver_Init( FT_Module  module )        /* T42_Driver */
    476   {
    477     T42_Driver  driver = (T42_Driver)module;
    478     FT_Module   ttmodule;
    479 
    480 
    481     ttmodule = FT_Get_Module( module->library, "truetype" );
    482     if ( !ttmodule )
    483     {
    484       FT_ERROR(( "T42_Driver_Init: cannot access `truetype' module\n" ));
    485       return FT_THROW( Missing_Module );
    486     }
    487 
    488     driver->ttclazz = (FT_Driver_Class)ttmodule->clazz;
    489 
    490     return FT_Err_Ok;
    491   }
    492 
    493 
    494   FT_LOCAL_DEF( void )
    495   T42_Driver_Done( FT_Module  module )
    496   {
    497     FT_UNUSED( module );
    498   }
    499 
    500 
    501   FT_LOCAL_DEF( FT_Error )
    502   T42_Size_Init( FT_Size  size )         /* T42_Size */
    503   {
    504     T42_Size  t42size = (T42_Size)size;
    505     FT_Face   face    = size->face;
    506     T42_Face  t42face = (T42_Face)face;
    507     FT_Size   ttsize;
    508     FT_Error  error;
    509 
    510 
    511     error = FT_New_Size( t42face->ttf_face, &ttsize );
    512     t42size->ttsize = ttsize;
    513 
    514     FT_Activate_Size( ttsize );
    515 
    516     return error;
    517   }
    518 
    519 
    520   FT_LOCAL_DEF( FT_Error )
    521   T42_Size_Request( FT_Size          t42size,      /* T42_Size */
    522                     FT_Size_Request  req )
    523   {
    524     T42_Size  size = (T42_Size)t42size;
    525     T42_Face  face = (T42_Face)t42size->face;
    526     FT_Error  error;
    527 
    528 
    529     FT_Activate_Size( size->ttsize );
    530 
    531     error = FT_Request_Size( face->ttf_face, req );
    532     if ( !error )
    533       t42size->metrics = face->ttf_face->size->metrics;
    534 
    535     return error;
    536   }
    537 
    538 
    539   FT_LOCAL_DEF( FT_Error )
    540   T42_Size_Select( FT_Size   t42size,         /* T42_Size */
    541                    FT_ULong  strike_index )
    542   {
    543     T42_Size  size = (T42_Size)t42size;
    544     T42_Face  face = (T42_Face)t42size->face;
    545     FT_Error  error;
    546 
    547 
    548     FT_Activate_Size( size->ttsize );
    549 
    550     error = FT_Select_Size( face->ttf_face, (FT_Int)strike_index );
    551     if ( !error )
    552       t42size->metrics = face->ttf_face->size->metrics;
    553 
    554     return error;
    555 
    556   }
    557 
    558 
    559   FT_LOCAL_DEF( void )
    560   T42_Size_Done( FT_Size  t42size )             /* T42_Size */
    561   {
    562     T42_Size     size    = (T42_Size)t42size;
    563     FT_Face      face    = t42size->face;
    564     T42_Face     t42face = (T42_Face)face;
    565     FT_ListNode  node;
    566 
    567 
    568     node = FT_List_Find( &t42face->ttf_face->sizes_list, size->ttsize );
    569     if ( node )
    570     {
    571       FT_Done_Size( size->ttsize );
    572       size->ttsize = NULL;
    573     }
    574   }
    575 
    576 
    577   FT_LOCAL_DEF( FT_Error )
    578   T42_GlyphSlot_Init( FT_GlyphSlot  t42slot )        /* T42_GlyphSlot */
    579   {
    580     T42_GlyphSlot  slot    = (T42_GlyphSlot)t42slot;
    581     FT_Face        face    = t42slot->face;
    582     T42_Face       t42face = (T42_Face)face;
    583     FT_GlyphSlot   ttslot;
    584     FT_Error       error   = FT_Err_Ok;
    585 
    586 
    587     if ( !face->glyph )
    588     {
    589       /* First glyph slot for this face */
    590       slot->ttslot = t42face->ttf_face->glyph;
    591     }
    592     else
    593     {
    594       error = FT_New_GlyphSlot( t42face->ttf_face, &ttslot );
    595       slot->ttslot = ttslot;
    596     }
    597 
    598     return error;
    599   }
    600 
    601 
    602   FT_LOCAL_DEF( void )
    603   T42_GlyphSlot_Done( FT_GlyphSlot  t42slot )       /* T42_GlyphSlot */
    604   {
    605     T42_GlyphSlot  slot = (T42_GlyphSlot)t42slot;
    606 
    607 
    608     FT_Done_GlyphSlot( slot->ttslot );
    609   }
    610 
    611 
    612   static void
    613   t42_glyphslot_clear( FT_GlyphSlot  slot )
    614   {
    615     /* free bitmap if needed */
    616     ft_glyphslot_free_bitmap( slot );
    617 
    618     /* clear all public fields in the glyph slot */
    619     FT_ZERO( &slot->metrics );
    620     FT_ZERO( &slot->outline );
    621     FT_ZERO( &slot->bitmap );
    622 
    623     slot->bitmap_left   = 0;
    624     slot->bitmap_top    = 0;
    625     slot->num_subglyphs = 0;
    626     slot->subglyphs     = NULL;
    627     slot->control_data  = NULL;
    628     slot->control_len   = 0;
    629     slot->other         = NULL;
    630     slot->format        = FT_GLYPH_FORMAT_NONE;
    631 
    632     slot->linearHoriAdvance = 0;
    633     slot->linearVertAdvance = 0;
    634   }
    635 
    636 
    637   FT_LOCAL_DEF( FT_Error )
    638   T42_GlyphSlot_Load( FT_GlyphSlot  glyph,
    639                       FT_Size       size,
    640                       FT_UInt       glyph_index,
    641                       FT_Int32      load_flags )
    642   {
    643     FT_Error         error;
    644     T42_GlyphSlot    t42slot = (T42_GlyphSlot)glyph;
    645     T42_Size         t42size = (T42_Size)size;
    646     T42_Face         t42face = (T42_Face)size->face;
    647     FT_Driver_Class  ttclazz = ((T42_Driver)glyph->face->driver)->ttclazz;
    648 
    649 
    650     FT_TRACE1(( "T42_GlyphSlot_Load: glyph index %d\n", glyph_index ));
    651 
    652     /* map T42 glyph index to embedded TTF's glyph index */
    653     glyph_index = (FT_UInt)ft_strtol(
    654                     (const char *)t42face->type1.charstrings[glyph_index],
    655                     NULL, 10 );
    656 
    657     t42_glyphslot_clear( t42slot->ttslot );
    658     error = ttclazz->load_glyph( t42slot->ttslot,
    659                                  t42size->ttsize,
    660                                  glyph_index,
    661                                  load_flags | FT_LOAD_NO_BITMAP );
    662 
    663     if ( !error )
    664     {
    665       glyph->metrics = t42slot->ttslot->metrics;
    666 
    667       glyph->linearHoriAdvance = t42slot->ttslot->linearHoriAdvance;
    668       glyph->linearVertAdvance = t42slot->ttslot->linearVertAdvance;
    669 
    670       glyph->format  = t42slot->ttslot->format;
    671       glyph->outline = t42slot->ttslot->outline;
    672 
    673       glyph->bitmap      = t42slot->ttslot->bitmap;
    674       glyph->bitmap_left = t42slot->ttslot->bitmap_left;
    675       glyph->bitmap_top  = t42slot->ttslot->bitmap_top;
    676 
    677       glyph->num_subglyphs = t42slot->ttslot->num_subglyphs;
    678       glyph->subglyphs     = t42slot->ttslot->subglyphs;
    679 
    680       glyph->control_data  = t42slot->ttslot->control_data;
    681       glyph->control_len   = t42slot->ttslot->control_len;
    682     }
    683 
    684     return error;
    685   }
    686 
    687 
    688 /* END */
    689