From 51daa4feb18fce52a3c5d40bd6c9969203ec6b28 Mon Sep 17 00:00:00 2001 From: Werner Lemberg Date: Sun, 18 May 2003 22:40:11 +0000 Subject: [PATCH] * src/raster/ftraster.c (Insert_Y_Turn): Fix overflow test. * include/freetype/config/ftoption.h [FT_CONFIG_OPTION_MAC_FONTS]: New macro. * src/base/ftobjs.c: Use it to control mac font support on non-mac platforms. Implement partial support of Mac fonts on non-Mac platforms. * src/base/ftobjs.c (memory_stream_close, new_memory_stream, open_face_from_buffer, Mac_Read_POST_Resource, Mac_Read_sfnt_Resource, IsMacResource, IsMacBinary, load_mac_face) [!FT_MACINTOSH]: New functions. (FT_Open_Face) [!FT_MACINTOSH]: Use load_mac_face. * src/base/ftobjs.c (FT_Load_Glyph): Scale linear advance width only if FT_FACE_FLAG_SCALABLE is set (otherwise we have a division by zero since FNT and friends don't define `face->units_per_EM'). --- ChangeLog | 27 ++ include/freetype/config/ftoption.h | 13 + src/base/ftobjs.c | 624 ++++++++++++++++++++++++++++- src/raster/ftraster.c | 8 +- 4 files changed, 656 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7d17814ee..465c8502f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2003-05-18 Yong Sun + + * src/raster/ftraster.c (Insert_Y_Turn): Fix overflow test. + +2003-05-18 Werner Lemberg + + * include/freetype/config/ftoption.h [FT_CONFIG_OPTION_MAC_FONTS]: + New macro. + * src/base/ftobjs.c: Use it to control mac font support on non-mac + platforms. + +2003-05-17 George Williams + + Implement partial support of Mac fonts on non-Mac platforms. + + * src/base/ftobjs.c (memory_stream_close, new_memory_stream, + open_face_from_buffer, Mac_Read_POST_Resource, + Mac_Read_sfnt_Resource, IsMacResource, IsMacBinary, load_mac_face) + [!FT_MACINTOSH]: New functions. + (FT_Open_Face) [!FT_MACINTOSH]: Use load_mac_face. + +2003-05-17 Werner Lemberg + + * src/base/ftobjs.c (FT_Load_Glyph): Scale linear advance width only + if FT_FACE_FLAG_SCALABLE is set (otherwise we have a division by + zero since FNT and friends don't define `face->units_per_EM'). + 2003-05-15 David Turner * src/base/fttrigon.c (FT_Vector_Rotate): Avoid rounding errors diff --git a/include/freetype/config/ftoption.h b/include/freetype/config/ftoption.h index dcf526ee6..c55404b25 100644 --- a/include/freetype/config/ftoption.h +++ b/include/freetype/config/ftoption.h @@ -217,6 +217,19 @@ FT_BEGIN_HEADER #define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST + /*************************************************************************/ + /* */ + /* Support for Mac fonts */ + /* */ + /* Define this macro if you want support for outline fonts in Mac */ + /* format (mac dfont, mac resource, macbinary containing a mac */ + /* resource) on non-Mac platforms. */ + /* */ + /* Note that the `FOND' resource isn't checked. */ + /* */ +#define FT_CONFIG_OPTION_MAC_FONTS + + /*************************************************************************/ /* */ /* Allow the use of FT_Incremental_Interface to load typefaces that */ diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index 42e06790c..9d601e680 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -204,8 +204,8 @@ if ( slot->flags & FT_GLYPH_OWN_BITMAP ) { FT_Memory memory = FT_FACE_MEMORY( slot->face ); - - + + FT_FREE( slot->bitmap.buffer ); slot->flags &= ~FT_GLYPH_OWN_BITMAP; } @@ -223,11 +223,11 @@ FT_Byte* buffer ) { ft_glyphslot_free_bitmap( slot ); - + slot->bitmap.buffer = buffer; - + FT_ASSERT( (slot->flags & FT_GLYPH_OWN_BITMAP) == 0 ); - } + } FT_BASE_DEF( FT_Error ) @@ -235,15 +235,15 @@ FT_ULong size ) { FT_Memory memory = FT_FACE_MEMORY( slot->face ); - - + + if ( slot->flags & FT_GLYPH_OWN_BITMAP ) FT_FREE( slot->bitmap.buffer ); else slot->flags |= FT_GLYPH_OWN_BITMAP; - + return FT_MEM_ALLOC( slot->bitmap.buffer, size ); - } + } static void @@ -561,7 +561,8 @@ } /* compute the linear advance in 16.16 pixels */ - if ( ( load_flags & FT_LOAD_LINEAR_DESIGN ) == 0 ) + if ( ( load_flags & FT_LOAD_LINEAR_DESIGN ) == 0 && + ( face->face_flags & FT_FACE_FLAG_SCALABLE ) ) { FT_UInt EM = face->units_per_EM; FT_Size_Metrics* metrics = &face->size->metrics; @@ -977,6 +978,580 @@ } +#if !defined( FT_MACINTOSH ) && defined( FT_CONFIG_OPTION_MAC_FONTS ) + + /* The behavior here is very similar to that in base/ftmac.c, but it */ + /* is designed to work on non-mac systems, so no mac specific calls. */ + /* */ + /* We look at the file and determine if it is a mac dfont file or a mac */ + /* resource file, or a macbinary file containing a mac resource file. */ + /* */ + /* Unlike ftmac I'm not going to look at a `FOND'. I don't really see */ + /* the point, especially since there may be multiple `FOND' resources. */ + /* Instead I'll just look for `sfnt' and `POST' resources, ordered as */ + /* they occur in the file. */ + /* */ + /* Note that multiple `POST' resources do not mean multiple postscript */ + /* fonts; they all get jammed together to make what is essentially a */ + /* pfb file. */ + /* */ + /* We aren't interested in `NFNT' or `FONT' bitmap resources. */ + /* */ + /* As soon as we get an `sfnt' load it into memory and pass it off to */ + /* FT_Open_Face. */ + /* */ + /* If we have a (set of) `POST' resources, massage them into a (memory) */ + /* pfb file and pass that to FT_Open_Face. (As with ftmac.c I'm not */ + /* going to try to save the kerning info. After all that lives in the */ + /* `FOND' which isn't in the file containing the `POST' resources so */ + /* we don't really have access to it. */ + + + /* Finalizer for a memory stream; gets called by FT_Done_Face(). + It frees the memory it uses. */ + /* from ftmac.c */ + static void + memory_stream_close( FT_Stream stream ) + { + FT_Memory memory = stream->memory; + + + FT_FREE( stream->base ); + + stream->size = 0; + stream->base = 0; + stream->close = 0; + } + + + /* Create a new memory stream from a buffer and a size. */ + /* from ftmac.c */ + static FT_Error + new_memory_stream( FT_Library library, + FT_Byte* base, + FT_ULong size, + FT_Stream_CloseFunc close, + FT_Stream *astream ) + { + FT_Error error; + FT_Memory memory; + FT_Stream stream; + + + if ( !library ) + return FT_Err_Invalid_Library_Handle; + + if ( !base ) + return FT_Err_Invalid_Argument; + + *astream = 0; + memory = library->memory; + if ( FT_NEW( stream ) ) + goto Exit; + + FT_Stream_OpenMemory( stream, base, size ); + + stream->close = close; + + *astream = stream; + + Exit: + return error; + } + + + /* Create a new FT_Face given a buffer and a driver name. */ + /* from ftmac.c */ + static FT_Error + open_face_from_buffer( FT_Library library, + FT_Byte* base, + FT_ULong size, + FT_Long face_index, + const char* driver_name, + FT_Face *aface ) + { + FT_Open_Args args; + FT_Error error; + FT_Stream stream; + FT_Memory memory = library->memory; + + + error = new_memory_stream( library, + base, + size, + memory_stream_close, + &stream ); + if ( error ) + { + FT_FREE( base ); + return error; + } + + args.flags = FT_OPEN_STREAM; + args.stream = stream; + if ( driver_name ) + { + args.flags = args.flags | FT_OPEN_DRIVER; + args.driver = FT_Get_Module( library, driver_name ); + } + + error = FT_Open_Face( library, &args, face_index, aface ); + + if ( error == FT_Err_Ok ) + (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; + else + { + FT_Stream_Close( stream ); + FT_FREE( stream ); + } + + return error; + } + + + /* The resource header says we've got resource_cnt `POST' (type1) */ + /* resources in this file. They all need to be coalesced into */ + /* one lump which gets passed on to the type1 driver. */ + /* Here can be only one PostScript font in a file so face_index */ + /* must be 0 (or -1). */ + /* */ + static FT_Error + Mac_Read_POST_Resource( FT_Library library, + FT_Stream stream, + FT_Long resource_listoffset, + FT_Long resource_cnt, + FT_Long resource_data, + FT_Long face_index, + FT_Face *aface ) + { + FT_Error error = FT_Err_Cannot_Open_Resource; + FT_Memory memory = library->memory; + FT_Byte* pfb_data; + int i, type, flags, len; + FT_Long pfb_len, pfb_pos, pfb_lenpos; + FT_Long rlen, junk, temp; + FT_Long *offsets; + + + if ( face_index == -1 ) + face_index = 0; + if ( face_index != 0 ) + return FT_Err_Cannot_Open_Resource; + + if ( FT_ALLOC( offsets, (FT_Long)resource_cnt * sizeof ( FT_Long ) ) ) + return error; + + error = FT_Stream_Seek( stream, resource_listoffset ); + if ( error ) + goto Exit; + + /* Find all the POST resource offsets */ + for ( i = 0; i < resource_cnt; ++i ) + { + (void)FT_READ_USHORT( junk ); /* resource id */ + (void)FT_READ_USHORT( junk ); /* rsource name */ + if ( FT_READ_LONG( temp ) ) + goto Exit; + offsets[i] = resource_data + ( temp & 0xFFFFFFL ); + (void)FT_READ_LONG( junk ); /* mbz */ + } + + /* Find the length of all the POST resources, concatenated. Assume */ + /* worst case (each resource in its own section). */ + pfb_len = 0; + for ( i = 0; i < resource_cnt; ++i ) + { + error = FT_Stream_Seek( stream, offsets[i] ); + if ( error ) + goto Exit; + if ( FT_READ_LONG( temp ) ) + goto Exit; + pfb_len += temp + 6; + } + + if ( FT_ALLOC( pfb_data, (FT_Long)pfb_len + 2 ) ) + goto Exit; + + pfb_pos = 0; + pfb_data[pfb_pos++] = 0x80; + pfb_data[pfb_pos++] = 1; /* Ascii section */ + pfb_lenpos = pfb_pos; + pfb_data[pfb_pos++] = 0; /* 4-byte length, fill in later */ + pfb_data[pfb_pos++] = 0; + pfb_data[pfb_pos++] = 0; + pfb_data[pfb_pos++] = 0; + + len = 0; + type = 1; + for ( i = 0; i < resource_cnt; ++i ) + { + error = FT_Stream_Seek( stream, offsets[i] ); + if ( error ) + goto Exit2; + if ( FT_READ_LONG( rlen ) ) + goto Exit; + if ( FT_READ_USHORT( flags ) ) + goto Exit; + rlen -= 2; /* the flags are part of the resource */ + if ( ( flags >> 8 ) == type ) + len += rlen; + else + { + pfb_data[pfb_lenpos ] = len & 0xFF; + pfb_data[pfb_lenpos + 1] = ( len >> 8 ) & 0xFF; + pfb_data[pfb_lenpos + 2] = ( len >> 16 ) & 0xFF; + pfb_data[pfb_lenpos + 3] = ( len >> 24 ) & 0xFF; + + if ( ( flags >> 8 ) == 5 ) /* End of font mark */ + break; + + pfb_data[pfb_pos++] = 0x80; + + type = flags >> 8; + len = rlen; + + pfb_data[pfb_pos++] = type; + pfb_lenpos = pfb_pos; + pfb_data[pfb_pos++] = 0; /* 4-byte length, fill in later */ + pfb_data[pfb_pos++] = 0; + pfb_data[pfb_pos++] = 0; + pfb_data[pfb_pos++] = 0; + } + + error = FT_Stream_Read( stream, (FT_Byte *)pfb_data + pfb_pos, rlen ); + pfb_pos += rlen; + } + + pfb_data[pfb_pos++] = 0x80; + pfb_data[pfb_pos++] = 3; + + pfb_data[pfb_lenpos ] = len & 0xFF; + pfb_data[pfb_lenpos + 1] = ( len >> 8 ) & 0xFF; + pfb_data[pfb_lenpos + 2] = ( len >> 16 ) & 0xFF; + pfb_data[pfb_lenpos + 3] = ( len >> 24 ) & 0xFF; + + return open_face_from_buffer( library, + pfb_data, + pfb_pos, + face_index, + "type1", + aface ); + + Exit2: + FT_FREE( pfb_data ); + + Exit: + FT_FREE( offsets ); + return error; + } + + + /* The resource header says we've got resource_cnt `sfnt' */ + /* (TrueType/OpenType) resources in this file. Look through */ + /* them for the one indicated by face_index, load it into mem, */ + /* pass it on the the truetype driver and return it. */ + /* */ + static FT_Error + Mac_Read_sfnt_Resource( FT_Library library, + FT_Stream stream, + FT_Long resource_listoffset, + FT_Long resource_cnt, + FT_Long resource_data, + FT_Long face_index, + FT_Face *aface ) + { + FT_Memory memory = library->memory; + FT_Byte* sfnt_data; + FT_Error error; + int i; + FT_Long flag_offset= 0xFFFFFFL; + FT_Long rlen, junk; + int is_cff; + + + if ( face_index == -1 ) + face_index = 0; + if ( face_index >= resource_cnt ) + return FT_Err_Cannot_Open_Resource; + + error = FT_Stream_Seek( stream, resource_listoffset ); + if ( error ) + goto Exit; + + for ( i = 0; i <= face_index; ++i ) + { + (void)FT_READ_USHORT( junk ); /* resource id */ + (void)FT_READ_USHORT( junk ); /* rsource name */ + if ( FT_READ_LONG( flag_offset ) ) + goto Exit; + flag_offset &= 0xFFFFFFL; + (void)FT_READ_LONG( junk ); /* mbz */ + } + + if ( flag_offset == 0xFFFFFFL ) + return FT_Err_Cannot_Open_Resource; + + error = FT_Stream_Seek( stream, flag_offset + resource_data ); + if ( error ) + goto Exit; + if ( FT_READ_LONG( rlen ) ) + goto Exit; + if ( rlen == -1 ) + return FT_Err_Cannot_Open_Resource; + + if ( FT_ALLOC( sfnt_data, (FT_Long)rlen ) ) + return error; + error = FT_Stream_Read( stream, (FT_Byte *)sfnt_data, rlen ); + if ( error ) + goto Exit; + + is_cff = rlen > 4 && sfnt_data[0] == 'O' && + sfnt_data[1] == 'T' && + sfnt_data[2] == 'T' && + sfnt_data[3] == 'O'; + + error = open_face_from_buffer( library, + sfnt_data, + rlen, + face_index, + is_cff ? "cff" : "truetype", + aface ); + + Exit: + return error; + } + + + /* Check for a valid resource fork header, or a valid dfont */ + /* header. In a resource fork the first 16 bytes are repeated */ + /* at the location specified by bytes 4-7. In a dfont bytes */ + /* 4-7 point to 16 bytes of zeroes instead. */ + /* */ + static FT_Error + IsMacResource( FT_Library library, + FT_Stream stream, + FT_Long resource_offset, + FT_Long face_index, + FT_Face *aface ) + { + FT_Error error; + unsigned char head[16], head2[16]; + FT_Long rdata_pos, map_pos, rdata_len, map_len; + int allzeros, allmatch, i, cnt, subcnt; + FT_Long type_list, tag, rpos, junk; + + + error = FT_Stream_Seek( stream, resource_offset ); + if ( error ) + goto Exit; + error = FT_Stream_Read( stream, (FT_Byte *)head, 16 ); + if ( error ) + goto Exit; + + rdata_pos = resource_offset + ( ( head[0] << 24 ) | + ( head[1] << 16 ) | + ( head[2] << 8 ) | + head[3] ); + map_pos = resource_offset + ( ( head[4] << 24 ) | + ( head[5] << 16 ) | + ( head[6] << 8 ) | + head[7] ); + rdata_len = ( head[ 8] << 24 ) | + ( head[ 9] << 16 ) | + ( head[10] << 8 ) | + head[11]; + map_len = ( head[12] << 24 ) | + ( head[13] << 16 ) | + ( head[14] << 8 ) | + head[15]; + + if ( rdata_pos + rdata_len != map_pos || map_pos == resource_offset ) + return FT_Err_Unknown_File_Format; + + error = FT_Stream_Seek( stream, map_pos ); + if ( error ) + goto Exit; + + head2[15] = head[15] + 1; /* make it be different */ + + error = FT_Stream_Read( stream, (FT_Byte*)head2, 16 ); + if ( error ) + goto Exit; + + allzeros = 1; + allmatch = 1; + for ( i = 0; i < 16; ++i ) + { + if ( head2[i] != 0 ) + allzeros = 0; + if ( head2[i] != head[i] ) + allmatch = 0; + } + if ( !allzeros && !allmatch ) + return FT_Err_Unknown_File_Format; + + /* If we've gotten this far then it's probably a mac resource file. */ + /* Now, does it contain any interesting resources? */ + + (void)FT_READ_LONG( junk ); /* skip handle to next resource map */ + (void)FT_READ_USHORT( junk ); /* skip file resource number */ + (void)FT_READ_USHORT( junk ); /* skip attributes */ + + if ( FT_READ_USHORT( type_list ) ) + goto Exit; + if ( type_list == -1 ) + return FT_Err_Unknown_File_Format; + + error = FT_Stream_Seek( stream, map_pos + type_list ); + if ( error ) + goto Exit; + + if ( FT_READ_USHORT( cnt ) ) + goto Exit; + + ++cnt; + for ( i = 0; i < cnt; ++i ) + { + if ( FT_READ_LONG( tag ) || + FT_READ_USHORT( subcnt ) || + FT_READ_USHORT( rpos ) ) + goto Exit; + + ++subcnt; + rpos += map_pos + type_list; + if ( tag == FT_MAKE_TAG( 'P', 'O', 'S', 'T' ) ) + return Mac_Read_POST_Resource( library, + stream, + rpos, + subcnt, + rdata_pos, + face_index, + aface ); + else if ( tag == FT_MAKE_TAG( 's', 'f', 'n', 't' ) ) + return Mac_Read_sfnt_Resource( library, + stream, + rpos, + subcnt, + rdata_pos, + face_index, + aface ); + } + + error = FT_Err_Cannot_Open_Resource; /* this file contains no + interesting resources */ + Exit: + return error; + } + + + /* Check for a valid macbinary header, and if we find one */ + /* check that the (flattened) resource fork in it is valid. */ + /* */ + static FT_Error + IsMacBinary( FT_Library library, + FT_Stream stream, + FT_Long face_index, + FT_Face *aface ) + { + unsigned char header[128]; + FT_Error error; + FT_Long dlen, offset; + + + error = FT_Stream_Seek( stream, 0 ); + if ( error ) + goto Exit; + + error = FT_Stream_Read( stream, (FT_Byte*)header, 128 ); + if ( error ) + goto Exit; + + if ( header[ 0] != 0 || + header[74] != 0 || + header[82] != 0 || + header[ 1] == 0 || + header[ 1] > 33 || + header[63] != 0 || + header[2 + header[1]] != 0 ) + return FT_Err_Unknown_File_Format; + + dlen = ( header[0x53] << 24 ) | + ( header[0x54] << 16 ) | + ( header[0x55] << 8 ) | + header[0x56]; +#if 0 + rlen = ( header[0x57] << 24 ) | + ( header[0x58] << 16 ) | + ( header[0x59] << 8 ) | + header[0x5a]; +#endif /* 0 */ + offset = 128 + ( ( dlen + 127 ) & ~127 ); + + return IsMacResource( library, stream, offset, face_index, aface ); + + Exit: + return error; + } + + + /* Check for some macintosh formats */ + /* Is this a macbinary file? If so look at the resource fork. */ + /* Is this a mac dfont file? */ + /* Is this an old style resource fork? (in data) */ + /* Else if we're on Mac OS/X, open the resource fork explicitly. */ + /* */ + static FT_Error + load_mac_face( FT_Library library, + FT_Stream stream, + FT_Long face_index, + FT_Face *aface, + const FT_Open_Args *args ) + { + FT_Error error; + FT_UNUSED( args ); + + + error = IsMacBinary( library, stream, face_index, aface ); + if ( FT_ERROR_BASE( error ) == FT_Err_Unknown_File_Format ) + error = IsMacResource( library, stream, 0, face_index, aface ); + +#ifdef FT_MACINTOSH + + if ( ( FT_ERROR_BASE( error ) == FT_Err_Unknown_File_Format || + FT_ERROR_BASE( error ) == FT_Err_Invalid_Stream_Operation ) && + ( args->flags & FT_OPEN_PATHNAME ) ) + { + FT_Open_Args args2; + char* newpath; + FT_Memory memory; + FT_Stream stream2; + + + memory = library->memory; + + FT_ALLOC( newpath, strlen( args->pathname ) + strlen( "/rsrc" ) + 1 ); + strcpy( newpath, args->pathname ); + strcat( newpath, "/rsrc" ); + + args2.flags = FT_OPEN_PATHNAME; + args2.pathname = (char*)newpath; + error = ft_input_stream_new( library, &args2, &stream2 ); + if ( !error ) + { + error = IsMacResource( library, stream2, 0, face_index, aface ); + FT_Stream_Close( stream2 ); + } + FT_FREE( newpath ); + } + +#endif /* FT_MACINTOSH */ + + return error; + } + +#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */ + + /* documentation is in freetype.h */ FT_EXPORT_DEF( FT_Error ) @@ -1067,15 +1642,40 @@ } error = open_face( driver, stream, face_index, - num_params, params, &face ); + num_params, params, &face ); if ( !error ) goto Success; if ( FT_ERROR_BASE( error ) != FT_Err_Unknown_File_Format ) - goto Fail2; + goto Fail3; } } + Fail3: + /* If we are on the mac, and we get an FT_Err_Invalid_Stream_Operation */ + /* it may be because we have an empty data fork, so we need to check */ + /* the resource fork. */ + if ( FT_ERROR_BASE( error ) != FT_Err_Unknown_File_Format && + FT_ERROR_BASE( error ) != FT_Err_Invalid_Stream_Operation ) + goto Fail2; + +#ifndef FT_MACINTOSH + error = load_mac_face( library, stream, face_index, aface, args ); + if ( !error ) + { + /* We don't want to go to Success here. We've already done that. */ + /* On the other hand, if we succeeded we still need to close this */ + /* stream (we opened a different stream which extracted the */ + /* interesting information out of this stream here. That stream */ + /* will still be open and the face will point to it). */ + ft_input_stream_free( stream, external_stream ); + return error; + } + + if ( FT_ERROR_BASE( error ) != FT_Err_Unknown_File_Format ) + goto Fail2; +#endif /* !FT_MACINTOSH */ + /* no driver is able to handle this format */ error = FT_Err_Unknown_File_Format; diff --git a/src/raster/ftraster.c b/src/raster/ftraster.c index 654a81ec3..65b55585f 100644 --- a/src/raster/ftraster.c +++ b/src/raster/ftraster.c @@ -4,7 +4,7 @@ /* */ /* The FreeType glyph rasterizer (body). */ /* */ -/* Copyright 1996-2001, 2002 by */ +/* Copyright 1996-2001, 2002, 2003 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -687,8 +687,8 @@ static Bool Insert_Y_Turn( RAS_ARGS Int y ) { - PLong y_turns; - Int y2, n; + PLong y_turns; + Int y2, n; n = ras.numTurns - 1; @@ -710,12 +710,12 @@ if ( n < 0 ) { + ras.maxBuff--; if ( ras.maxBuff <= ras.top ) { ras.error = Raster_Err_Overflow; return FAILURE; } - ras.maxBuff--; ras.numTurns++; ras.sizeBuff[-ras.numTurns] = y; }