Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 1 | /***************************************************************************/ |
| 2 | /* */ |
Werner Lemberg | b5084e1 | 2000-10-28 17:10:06 +0000 | [diff] [blame] | 3 | /* t1gload.c */ |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 4 | /* */ |
Werner Lemberg | b5084e1 | 2000-10-28 17:10:06 +0000 | [diff] [blame] | 5 | /* Type 1 Glyph Loader (body). */ |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 6 | /* */ |
Werner Lemberg | 2a33275 | 2010-01-05 10:27:15 +0100 | [diff] [blame] | 7 | /* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 by */ |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 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 | |
Werner Lemberg | cc069be | 2000-12-08 16:17:16 +0000 | [diff] [blame] | 18 | |
David Turner | 19ed8af | 2000-12-08 02:42:29 +0000 | [diff] [blame] | 19 | #include <ft2build.h> |
David Turner | 8d3a401 | 2001-03-20 11:14:24 +0000 | [diff] [blame] | 20 | #include "t1gload.h" |
suzuki toshiya | f2647dd | 2009-09-26 00:48:37 +0900 | [diff] [blame] | 21 | #include FT_INTERNAL_CALC_H |
David Turner | 19ed8af | 2000-12-08 02:42:29 +0000 | [diff] [blame] | 22 | #include FT_INTERNAL_DEBUG_H |
| 23 | #include FT_INTERNAL_STREAM_H |
| 24 | #include FT_OUTLINE_H |
| 25 | #include FT_INTERNAL_POSTSCRIPT_AUX_H |
Werner Lemberg | cc069be | 2000-12-08 16:17:16 +0000 | [diff] [blame] | 26 | |
Werner Lemberg | 1f7f0e8 | 2001-06-06 17:30:41 +0000 | [diff] [blame] | 27 | #include "t1errors.h" |
| 28 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 29 | |
| 30 | /*************************************************************************/ |
| 31 | /* */ |
| 32 | /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
| 33 | /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ |
| 34 | /* messages during execution. */ |
| 35 | /* */ |
| 36 | #undef FT_COMPONENT |
Werner Lemberg | b5084e1 | 2000-10-28 17:10:06 +0000 | [diff] [blame] | 37 | #define FT_COMPONENT trace_t1gload |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 38 | |
| 39 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 40 | /*************************************************************************/ |
| 41 | /*************************************************************************/ |
| 42 | /*************************************************************************/ |
| 43 | /********** *********/ |
| 44 | /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/ |
| 45 | /********** *********/ |
| 46 | /********** The following code is in charge of computing *********/ |
| 47 | /********** the maximum advance width of the font. It *********/ |
| 48 | /********** quickly processes each glyph charstring to *********/ |
| 49 | /********** extract the value from either a `sbw' or `seac' *********/ |
| 50 | /********** operator. *********/ |
| 51 | /********** *********/ |
| 52 | /*************************************************************************/ |
| 53 | /*************************************************************************/ |
| 54 | /*************************************************************************/ |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 55 | |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 56 | |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 57 | FT_LOCAL_DEF( FT_Error ) |
| 58 | T1_Parse_Glyph_And_Get_Char_String( T1_Decoder decoder, |
| 59 | FT_UInt glyph_index, |
| 60 | FT_Data* char_string ) |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 61 | { |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 62 | T1_Face face = (T1_Face)decoder->builder.face; |
| 63 | T1_Font type1 = &face->type1; |
Werner Lemberg | efd4e87 | 2004-05-13 12:59:59 +0000 | [diff] [blame] | 64 | FT_Error error = T1_Err_Ok; |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 65 | |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 66 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
| 67 | FT_Incremental_InterfaceRec *inc = |
| 68 | face->root.internal->incremental_interface; |
| 69 | #endif |
| 70 | |
David Turner | 34f1c2f | 2000-08-23 22:47:44 +0000 | [diff] [blame] | 71 | |
| 72 | decoder->font_matrix = type1->font_matrix; |
| 73 | decoder->font_offset = type1->font_offset; |
Werner Lemberg | 8728f29 | 2000-08-23 17:32:42 +0000 | [diff] [blame] | 74 | |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 75 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 76 | |
| 77 | /* For incremental fonts get the character data using the */ |
| 78 | /* callback function. */ |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 79 | if ( inc ) |
| 80 | error = inc->funcs->get_glyph_data( inc->object, |
| 81 | glyph_index, char_string ); |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 82 | else |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 83 | |
Graham Asher | 46960df | 2003-02-13 17:49:27 +0000 | [diff] [blame] | 84 | #endif /* FT_CONFIG_OPTION_INCREMENTAL */ |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 85 | |
| 86 | /* For ordinary fonts get the character data stored in the face record. */ |
| 87 | { |
| 88 | char_string->pointer = type1->charstrings[glyph_index]; |
Werner Lemberg | fb4bfdd | 2003-12-13 18:42:31 +0000 | [diff] [blame] | 89 | char_string->length = (FT_Int)type1->charstrings_len[glyph_index]; |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 90 | } |
| 91 | |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 92 | if ( !error ) |
| 93 | error = decoder->funcs.parse_charstrings( |
| 94 | decoder, (FT_Byte*)char_string->pointer, |
| 95 | char_string->length ); |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 96 | |
| 97 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 98 | |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 99 | /* Incremental fonts can optionally override the metrics. */ |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 100 | if ( !error && inc && inc->funcs->get_glyph_metrics ) |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 101 | { |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 102 | FT_Incremental_MetricsRec metrics; |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 103 | |
Werner Lemberg | fb4bfdd | 2003-12-13 18:42:31 +0000 | [diff] [blame] | 104 | |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 105 | metrics.bearing_x = FIXED_TO_INT( decoder->builder.left_bearing.x ); |
Ken Sharp | f19e46f | 2010-01-27 10:04:50 +0100 | [diff] [blame] | 106 | metrics.bearing_y = 0; |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 107 | metrics.advance = FIXED_TO_INT( decoder->builder.advance.x ); |
Ken Sharp | 980b76e | 2010-02-10 07:02:43 +0100 | [diff] [blame] | 108 | metrics.advance_v = FIXED_TO_INT( decoder->builder.advance.y ); |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 109 | |
| 110 | error = inc->funcs->get_glyph_metrics( inc->object, |
| 111 | glyph_index, FALSE, &metrics ); |
| 112 | |
| 113 | decoder->builder.left_bearing.x = INT_TO_FIXED( metrics.bearing_x ); |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 114 | decoder->builder.advance.x = INT_TO_FIXED( metrics.advance ); |
Ken Sharp | 980b76e | 2010-02-10 07:02:43 +0100 | [diff] [blame] | 115 | decoder->builder.advance.y = INT_TO_FIXED( metrics.advance_v ); |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 116 | } |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 117 | |
Graham Asher | 46960df | 2003-02-13 17:49:27 +0000 | [diff] [blame] | 118 | #endif /* FT_CONFIG_OPTION_INCREMENTAL */ |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 119 | |
Werner Lemberg | fb4bfdd | 2003-12-13 18:42:31 +0000 | [diff] [blame] | 120 | return error; |
Graham Asher | bd8b085 | 2002-07-18 16:57:43 +0000 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | |
| 124 | FT_CALLBACK_DEF( FT_Error ) |
| 125 | T1_Parse_Glyph( T1_Decoder decoder, |
| 126 | FT_UInt glyph_index ) |
| 127 | { |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 128 | FT_Data glyph_data; |
David Turner | 87c0d30 | 2003-12-24 01:10:46 +0000 | [diff] [blame] | 129 | FT_Error error = T1_Parse_Glyph_And_Get_Char_String( |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 130 | decoder, glyph_index, &glyph_data ); |
| 131 | |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 132 | |
| 133 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
Werner Lemberg | fb4bfdd | 2003-12-13 18:42:31 +0000 | [diff] [blame] | 134 | |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 135 | if ( !error ) |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 136 | { |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 137 | T1_Face face = (T1_Face)decoder->builder.face; |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 138 | |
| 139 | |
Werner Lemberg | b442ca1 | 2003-04-23 15:50:27 +0000 | [diff] [blame] | 140 | if ( face->root.internal->incremental_interface ) |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 141 | face->root.internal->incremental_interface->funcs->free_glyph_data( |
| 142 | face->root.internal->incremental_interface->object, |
| 143 | &glyph_data ); |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 144 | } |
Werner Lemberg | fb4bfdd | 2003-12-13 18:42:31 +0000 | [diff] [blame] | 145 | |
Graham Asher | 46960df | 2003-02-13 17:49:27 +0000 | [diff] [blame] | 146 | #endif /* FT_CONFIG_OPTION_INCREMENTAL */ |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 147 | |
| 148 | return error; |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 149 | } |
| 150 | |
Werner Lemberg | 8728f29 | 2000-08-23 17:32:42 +0000 | [diff] [blame] | 151 | |
David Turner | bc82f1b | 2002-03-01 02:26:22 +0000 | [diff] [blame] | 152 | FT_LOCAL_DEF( FT_Error ) |
Werner Lemberg | 4a2305c | 2001-06-28 07:17:51 +0000 | [diff] [blame] | 153 | T1_Compute_Max_Advance( T1_Face face, |
Werner Lemberg | 7386197 | 2003-06-05 04:31:05 +0000 | [diff] [blame] | 154 | FT_Pos* max_advance ) |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 155 | { |
Werner Lemberg | efa9961 | 2002-04-01 22:01:46 +0000 | [diff] [blame] | 156 | FT_Error error; |
| 157 | T1_DecoderRec decoder; |
| 158 | FT_Int glyph_index; |
| 159 | T1_Font type1 = &face->type1; |
David Turner | 4e7eeee | 2002-02-28 16:10:29 +0000 | [diff] [blame] | 160 | PSAux_Service psaux = (PSAux_Service)face->psaux; |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 161 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 162 | |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 163 | FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) ); |
| 164 | |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 165 | *max_advance = 0; |
| 166 | |
Werner Lemberg | 8728f29 | 2000-08-23 17:32:42 +0000 | [diff] [blame] | 167 | /* initialize load decoder */ |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 168 | error = psaux->t1_decoder_funcs->init( &decoder, |
| 169 | (FT_Face)face, |
| 170 | 0, /* size */ |
| 171 | 0, /* glyph slot */ |
| 172 | (FT_Byte**)type1->glyph_names, |
| 173 | face->blend, |
David Turner | 5893c1b | 2001-10-18 11:49:26 +0000 | [diff] [blame] | 174 | 0, |
Werner Lemberg | 6b5c669 | 2002-09-05 15:10:54 +0000 | [diff] [blame] | 175 | FT_RENDER_MODE_NORMAL, |
Werner Lemberg | b5084e1 | 2000-10-28 17:10:06 +0000 | [diff] [blame] | 176 | T1_Parse_Glyph ); |
Werner Lemberg | d1b7475 | 2000-08-24 16:29:15 +0000 | [diff] [blame] | 177 | if ( error ) |
David Turner | 34f1c2f | 2000-08-23 22:47:44 +0000 | [diff] [blame] | 178 | return error; |
Werner Lemberg | e4b32a5 | 2000-10-31 20:42:18 +0000 | [diff] [blame] | 179 | |
David Turner | 0dd3434 | 2000-02-02 12:20:53 +0000 | [diff] [blame] | 180 | decoder.builder.metrics_only = 1; |
| 181 | decoder.builder.load_points = 0; |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 182 | |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 183 | decoder.num_subrs = type1->num_subrs; |
| 184 | decoder.subrs = type1->subrs; |
| 185 | decoder.subrs_len = type1->subrs_len; |
| 186 | |
| 187 | decoder.buildchar = face->buildchar; |
| 188 | decoder.len_buildchar = face->len_buildchar; |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 189 | |
David Turner | 11cfdd0 | 2002-04-17 09:37:59 +0000 | [diff] [blame] | 190 | *max_advance = 0; |
| 191 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 192 | /* for each glyph, parse the glyph charstring and extract */ |
| 193 | /* the advance width */ |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 194 | for ( glyph_index = 0; glyph_index < type1->num_glyphs; glyph_index++ ) |
| 195 | { |
| 196 | /* now get load the unscaled outline */ |
Werner Lemberg | b5084e1 | 2000-10-28 17:10:06 +0000 | [diff] [blame] | 197 | error = T1_Parse_Glyph( &decoder, glyph_index ); |
David Turner | 11cfdd0 | 2002-04-17 09:37:59 +0000 | [diff] [blame] | 198 | if ( glyph_index == 0 || decoder.builder.advance.x > *max_advance ) |
| 199 | *max_advance = decoder.builder.advance.x; |
David Turner | 87c0d30 | 2003-12-24 01:10:46 +0000 | [diff] [blame] | 200 | |
Werner Lemberg | 7386197 | 2003-06-05 04:31:05 +0000 | [diff] [blame] | 201 | /* ignore the error if one occurred - skip to next glyph */ |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 202 | } |
| 203 | |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 204 | psaux->t1_decoder_funcs->done( &decoder ); |
| 205 | |
Werner Lemberg | 1429db6 | 2001-04-02 23:54:01 +0000 | [diff] [blame] | 206 | return T1_Err_Ok; |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | |
David Turner | bc82f1b | 2002-03-01 02:26:22 +0000 | [diff] [blame] | 210 | FT_LOCAL_DEF( FT_Error ) |
David Turner | 28534d6 | 2008-09-01 21:35:21 +0000 | [diff] [blame] | 211 | T1_Get_Advances( T1_Face face, |
| 212 | FT_UInt first, |
| 213 | FT_UInt count, |
| 214 | FT_ULong load_flags, |
| 215 | FT_Fixed* advances ) |
| 216 | { |
| 217 | T1_DecoderRec decoder; |
| 218 | T1_Font type1 = &face->type1; |
| 219 | PSAux_Service psaux = (PSAux_Service)face->psaux; |
| 220 | FT_UInt nn; |
| 221 | FT_Error error; |
| 222 | |
Werner Lemberg | b211651 | 2008-09-12 16:27:48 +0000 | [diff] [blame] | 223 | |
| 224 | if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) |
| 225 | { |
| 226 | for ( nn = 0; nn < count; nn++ ) |
| 227 | advances[nn] = 0; |
| 228 | |
| 229 | return T1_Err_Ok; |
David Turner | 28534d6 | 2008-09-01 21:35:21 +0000 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | error = psaux->t1_decoder_funcs->init( &decoder, |
| 233 | (FT_Face)face, |
| 234 | 0, /* size */ |
| 235 | 0, /* glyph slot */ |
| 236 | (FT_Byte**)type1->glyph_names, |
| 237 | face->blend, |
| 238 | 0, |
| 239 | FT_RENDER_MODE_NORMAL, |
| 240 | T1_Parse_Glyph ); |
| 241 | if ( error ) |
| 242 | return error; |
| 243 | |
David Turner | 28534d6 | 2008-09-01 21:35:21 +0000 | [diff] [blame] | 244 | decoder.builder.metrics_only = 1; |
| 245 | decoder.builder.load_points = 0; |
| 246 | |
Werner Lemberg | b211651 | 2008-09-12 16:27:48 +0000 | [diff] [blame] | 247 | decoder.num_subrs = type1->num_subrs; |
| 248 | decoder.subrs = type1->subrs; |
| 249 | decoder.subrs_len = type1->subrs_len; |
David Turner | 28534d6 | 2008-09-01 21:35:21 +0000 | [diff] [blame] | 250 | |
| 251 | decoder.buildchar = face->buildchar; |
| 252 | decoder.len_buildchar = face->len_buildchar; |
| 253 | |
| 254 | for ( nn = 0; nn < count; nn++ ) |
| 255 | { |
| 256 | error = T1_Parse_Glyph( &decoder, first + nn ); |
Werner Lemberg | b211651 | 2008-09-12 16:27:48 +0000 | [diff] [blame] | 257 | if ( !error ) |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 258 | advances[nn] = FIXED_TO_INT( decoder.builder.advance.x ); |
David Turner | 28534d6 | 2008-09-01 21:35:21 +0000 | [diff] [blame] | 259 | else |
| 260 | advances[nn] = 0; |
| 261 | } |
Werner Lemberg | b211651 | 2008-09-12 16:27:48 +0000 | [diff] [blame] | 262 | |
David Turner | 28534d6 | 2008-09-01 21:35:21 +0000 | [diff] [blame] | 263 | return T1_Err_Ok; |
| 264 | } |
| 265 | |
| 266 | |
| 267 | FT_LOCAL_DEF( FT_Error ) |
Werner Lemberg | 4a2305c | 2001-06-28 07:17:51 +0000 | [diff] [blame] | 268 | T1_Load_Glyph( T1_GlyphSlot glyph, |
| 269 | T1_Size size, |
David Turner | 609e28c | 2002-04-19 15:13:47 +0000 | [diff] [blame] | 270 | FT_UInt glyph_index, |
David Turner | d1245c0 | 2002-08-27 22:34:20 +0000 | [diff] [blame] | 271 | FT_Int32 load_flags ) |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 272 | { |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 273 | FT_Error error; |
David Turner | 4e7eeee | 2002-02-28 16:10:29 +0000 | [diff] [blame] | 274 | T1_DecoderRec decoder; |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 275 | T1_Face face = (T1_Face)glyph->root.face; |
| 276 | FT_Bool hinting; |
Werner Lemberg | efa9961 | 2002-04-01 22:01:46 +0000 | [diff] [blame] | 277 | T1_Font type1 = &face->type1; |
David Turner | 4e7eeee | 2002-02-28 16:10:29 +0000 | [diff] [blame] | 278 | PSAux_Service psaux = (PSAux_Service)face->psaux; |
| 279 | const T1_Decoder_Funcs decoder_funcs = psaux->t1_decoder_funcs; |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 280 | |
David Turner | 34f1c2f | 2000-08-23 22:47:44 +0000 | [diff] [blame] | 281 | FT_Matrix font_matrix; |
| 282 | FT_Vector font_offset; |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 283 | FT_Data glyph_data; |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 284 | FT_Bool must_finish_decoder = FALSE; |
Werner Lemberg | 319c00d | 2003-04-23 19:48:24 +0000 | [diff] [blame] | 285 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 286 | FT_Bool glyph_data_loaded = 0; |
Werner Lemberg | 319c00d | 2003-04-23 19:48:24 +0000 | [diff] [blame] | 287 | #endif |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 288 | |
Werner Lemberg | efa9961 | 2002-04-01 22:01:46 +0000 | [diff] [blame] | 289 | |
Werner Lemberg | 2a33275 | 2010-01-05 10:27:15 +0100 | [diff] [blame] | 290 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
| 291 | if ( glyph_index >= (FT_UInt)face->root.num_glyphs && |
Werner Lemberg | 1fc440b | 2010-01-05 13:39:37 +0100 | [diff] [blame] | 292 | !face->root.internal->incremental_interface ) |
Werner Lemberg | 2a33275 | 2010-01-05 10:27:15 +0100 | [diff] [blame] | 293 | #else |
Werner Lemberg | 913a365 | 2006-11-19 09:19:17 +0000 | [diff] [blame] | 294 | if ( glyph_index >= (FT_UInt)face->root.num_glyphs ) |
Werner Lemberg | 2a33275 | 2010-01-05 10:27:15 +0100 | [diff] [blame] | 295 | #endif /* FT_CONFIG_OPTION_INCREMENTAL */ |
Werner Lemberg | 913a365 | 2006-11-19 09:19:17 +0000 | [diff] [blame] | 296 | { |
| 297 | error = T1_Err_Invalid_Argument; |
| 298 | goto Exit; |
| 299 | } |
| 300 | |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 301 | FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) ); |
| 302 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 303 | if ( load_flags & FT_LOAD_NO_RECURSE ) |
David Turner | ed7f62a | 2000-03-28 11:19:28 +0000 | [diff] [blame] | 304 | load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; |
| 305 | |
Werner Lemberg | b66efef | 2009-03-12 08:07:49 +0000 | [diff] [blame] | 306 | if ( size ) |
| 307 | { |
| 308 | glyph->x_scale = size->root.metrics.x_scale; |
| 309 | glyph->y_scale = size->root.metrics.y_scale; |
| 310 | } |
| 311 | else |
| 312 | { |
| 313 | glyph->x_scale = 0x10000L; |
| 314 | glyph->y_scale = 0x10000L; |
| 315 | } |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 316 | |
| 317 | glyph->root.outline.n_points = 0; |
| 318 | glyph->root.outline.n_contours = 0; |
Werner Lemberg | e4b32a5 | 2000-10-31 20:42:18 +0000 | [diff] [blame] | 319 | |
David Turner | 8edbcab | 2001-06-19 08:28:24 +0000 | [diff] [blame] | 320 | hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 && |
| 321 | ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 322 | |
David Turner | b08fe2d | 2002-08-27 20:20:29 +0000 | [diff] [blame] | 323 | glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 324 | |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 325 | error = decoder_funcs->init( &decoder, |
| 326 | (FT_Face)face, |
| 327 | (FT_Size)size, |
| 328 | (FT_GlyphSlot)glyph, |
| 329 | (FT_Byte**)type1->glyph_names, |
| 330 | face->blend, |
Werner Lemberg | c3b2160 | 2001-12-05 01:22:05 +0000 | [diff] [blame] | 331 | FT_BOOL( hinting ), |
Werner Lemberg | c846eac | 2004-02-19 21:39:58 +0000 | [diff] [blame] | 332 | FT_LOAD_TARGET_MODE( load_flags ), |
Werner Lemberg | b5084e1 | 2000-10-28 17:10:06 +0000 | [diff] [blame] | 333 | T1_Parse_Glyph ); |
Werner Lemberg | 8728f29 | 2000-08-23 17:32:42 +0000 | [diff] [blame] | 334 | if ( error ) |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 335 | goto Exit; |
Werner Lemberg | e4b32a5 | 2000-10-31 20:42:18 +0000 | [diff] [blame] | 336 | |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 337 | must_finish_decoder = TRUE; |
| 338 | |
Werner Lemberg | 8eb0353 | 2001-06-19 23:03:41 +0000 | [diff] [blame] | 339 | decoder.builder.no_recurse = FT_BOOL( |
| 340 | ( load_flags & FT_LOAD_NO_RECURSE ) != 0 ); |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 341 | |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 342 | decoder.num_subrs = type1->num_subrs; |
| 343 | decoder.subrs = type1->subrs; |
| 344 | decoder.subrs_len = type1->subrs_len; |
| 345 | |
| 346 | decoder.buildchar = face->buildchar; |
| 347 | decoder.len_buildchar = face->len_buildchar; |
Werner Lemberg | e4b32a5 | 2000-10-31 20:42:18 +0000 | [diff] [blame] | 348 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 349 | /* now load the unscaled outline */ |
Werner Lemberg | 7f74a52 | 2002-07-26 09:09:10 +0000 | [diff] [blame] | 350 | error = T1_Parse_Glyph_And_Get_Char_String( &decoder, glyph_index, |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 351 | &glyph_data ); |
Werner Lemberg | 8728f29 | 2000-08-23 17:32:42 +0000 | [diff] [blame] | 352 | if ( error ) |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 353 | goto Exit; |
Werner Lemberg | 319c00d | 2003-04-23 19:48:24 +0000 | [diff] [blame] | 354 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 355 | glyph_data_loaded = 1; |
Werner Lemberg | 319c00d | 2003-04-23 19:48:24 +0000 | [diff] [blame] | 356 | #endif |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 357 | |
David Turner | 34f1c2f | 2000-08-23 22:47:44 +0000 | [diff] [blame] | 358 | font_matrix = decoder.font_matrix; |
| 359 | font_offset = decoder.font_offset; |
| 360 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 361 | /* save new glyph tables */ |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 362 | decoder_funcs->done( &decoder ); |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 363 | |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 364 | must_finish_decoder = FALSE; |
| 365 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 366 | /* now, set the metrics -- this is rather simple, as */ |
| 367 | /* the left side bearing is the xMin, and the top side */ |
| 368 | /* bearing the yMax */ |
| 369 | if ( !error ) |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 370 | { |
David Turner | b08fe2d | 2002-08-27 20:20:29 +0000 | [diff] [blame] | 371 | glyph->root.outline.flags &= FT_OUTLINE_OWNER; |
| 372 | glyph->root.outline.flags |= FT_OUTLINE_REVERSE_FILL; |
Werner Lemberg | e4b32a5 | 2000-10-31 20:42:18 +0000 | [diff] [blame] | 373 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 374 | /* for composite glyphs, return only left side bearing and */ |
| 375 | /* advance width */ |
David Turner | ed7f62a | 2000-03-28 11:19:28 +0000 | [diff] [blame] | 376 | if ( load_flags & FT_LOAD_NO_RECURSE ) |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 377 | { |
David Turner | 54e7574 | 2000-11-04 02:52:02 +0000 | [diff] [blame] | 378 | FT_Slot_Internal internal = glyph->root.internal; |
Werner Lemberg | 415235d | 2001-06-28 17:49:10 +0000 | [diff] [blame] | 379 | |
| 380 | |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 381 | glyph->root.metrics.horiBearingX = |
| 382 | FIXED_TO_INT( decoder.builder.left_bearing.x ); |
| 383 | glyph->root.metrics.horiAdvance = |
| 384 | FIXED_TO_INT( decoder.builder.advance.x ); |
| 385 | |
| 386 | internal->glyph_matrix = font_matrix; |
| 387 | internal->glyph_delta = font_offset; |
| 388 | internal->glyph_transformed = 1; |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 389 | } |
David Turner | ed7f62a | 2000-03-28 11:19:28 +0000 | [diff] [blame] | 390 | else |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 391 | { |
Werner Lemberg | 8728f29 | 2000-08-23 17:32:42 +0000 | [diff] [blame] | 392 | FT_BBox cbox; |
| 393 | FT_Glyph_Metrics* metrics = &glyph->root.metrics; |
Werner Lemberg | e8ff769 | 2003-08-06 04:40:48 +0000 | [diff] [blame] | 394 | FT_Vector advance; |
David Turner | e49ab25 | 2000-05-16 23:44:38 +0000 | [diff] [blame] | 395 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 396 | |
David Turner | ed7f62a | 2000-03-28 11:19:28 +0000 | [diff] [blame] | 397 | /* copy the _unscaled_ advance width */ |
Werner Lemberg | 11cb8c3 | 2009-06-22 16:56:47 +0200 | [diff] [blame] | 398 | metrics->horiAdvance = |
| 399 | FIXED_TO_INT( decoder.builder.advance.x ); |
| 400 | glyph->root.linearHoriAdvance = |
| 401 | FIXED_TO_INT( decoder.builder.advance.x ); |
David Turner | 54e7574 | 2000-11-04 02:52:02 +0000 | [diff] [blame] | 402 | glyph->root.internal->glyph_transformed = 0; |
David Turner | e49ab25 | 2000-05-16 23:44:38 +0000 | [diff] [blame] | 403 | |
Ken Sharp | 63e7aac | 2010-02-10 07:13:27 +0100 | [diff] [blame] | 404 | if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) |
| 405 | { |
| 406 | /* make up vertical ones */ |
| 407 | metrics->vertAdvance = ( face->type1.font_bbox.yMax - |
| 408 | face->type1.font_bbox.yMin ) >> 16; |
| 409 | glyph->root.linearVertAdvance = metrics->vertAdvance; |
| 410 | } |
| 411 | else |
| 412 | { |
| 413 | metrics->vertAdvance = |
| 414 | FIXED_TO_INT( decoder.builder.advance.y ); |
| 415 | glyph->root.linearVertAdvance = |
| 416 | FIXED_TO_INT( decoder.builder.advance.y ); |
| 417 | } |
David Turner | b881717 | 2000-09-15 17:17:17 +0000 | [diff] [blame] | 418 | |
David Turner | b08fe2d | 2002-08-27 20:20:29 +0000 | [diff] [blame] | 419 | glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; |
David Turner | e49ab25 | 2000-05-16 23:44:38 +0000 | [diff] [blame] | 420 | |
David Turner | d8723b4 | 2000-04-14 11:29:14 +0000 | [diff] [blame] | 421 | if ( size && size->root.metrics.y_ppem < 24 ) |
David Turner | b08fe2d | 2002-08-27 20:20:29 +0000 | [diff] [blame] | 422 | glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION; |
David Turner | 82942cc | 2000-03-30 08:43:03 +0000 | [diff] [blame] | 423 | |
David Turner | 85eb669 | 2001-12-20 09:36:21 +0000 | [diff] [blame] | 424 | #if 1 |
Werner Lemberg | c3b2160 | 2001-12-05 01:22:05 +0000 | [diff] [blame] | 425 | /* apply the font matrix, if any */ |
David Turner | 281679d | 2006-05-17 13:34:21 +0000 | [diff] [blame] | 426 | if ( font_matrix.xx != 0x10000L || font_matrix.yy != font_matrix.xx || |
| 427 | font_matrix.xy != 0 || font_matrix.yx != 0 ) |
| 428 | FT_Outline_Transform( &glyph->root.outline, &font_matrix ); |
David Turner | 24d7024 | 2000-08-17 01:09:06 +0000 | [diff] [blame] | 429 | |
David Turner | 281679d | 2006-05-17 13:34:21 +0000 | [diff] [blame] | 430 | if ( font_offset.x || font_offset.y ) |
| 431 | FT_Outline_Translate( &glyph->root.outline, |
| 432 | font_offset.x, |
| 433 | font_offset.y ); |
Werner Lemberg | e8ff769 | 2003-08-06 04:40:48 +0000 | [diff] [blame] | 434 | |
| 435 | advance.x = metrics->horiAdvance; |
Werner Lemberg | e52c4ba | 2003-08-06 17:27:14 +0000 | [diff] [blame] | 436 | advance.y = 0; |
| 437 | FT_Vector_Transform( &advance, &font_matrix ); |
| 438 | metrics->horiAdvance = advance.x + font_offset.x; |
| 439 | advance.x = 0; |
Werner Lemberg | e8ff769 | 2003-08-06 04:40:48 +0000 | [diff] [blame] | 440 | advance.y = metrics->vertAdvance; |
| 441 | FT_Vector_Transform( &advance, &font_matrix ); |
Werner Lemberg | e52c4ba | 2003-08-06 17:27:14 +0000 | [diff] [blame] | 442 | metrics->vertAdvance = advance.y + font_offset.y; |
David Turner | 5893c1b | 2001-10-18 11:49:26 +0000 | [diff] [blame] | 443 | #endif |
David Turner | e49ab25 | 2000-05-16 23:44:38 +0000 | [diff] [blame] | 444 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 445 | if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ) |
David Turner | ed7f62a | 2000-03-28 11:19:28 +0000 | [diff] [blame] | 446 | { |
| 447 | /* scale the outline and the metrics */ |
David Turner | f9b8dec | 2000-06-16 19:34:52 +0000 | [diff] [blame] | 448 | FT_Int n; |
David Turner | f0df85b | 2000-06-22 00:17:42 +0000 | [diff] [blame] | 449 | FT_Outline* cur = decoder.builder.base; |
David Turner | f9b8dec | 2000-06-16 19:34:52 +0000 | [diff] [blame] | 450 | FT_Vector* vec = cur->points; |
| 451 | FT_Fixed x_scale = glyph->x_scale; |
| 452 | FT_Fixed y_scale = glyph->y_scale; |
David Turner | e49ab25 | 2000-05-16 23:44:38 +0000 | [diff] [blame] | 453 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 454 | |
Werner Lemberg | c3b2160 | 2001-12-05 01:22:05 +0000 | [diff] [blame] | 455 | /* First of all, scale the points, if we are not hinting */ |
Werner Lemberg | 6c99045 | 2005-11-18 21:10:59 +0000 | [diff] [blame] | 456 | if ( !hinting || ! decoder.builder.hints_funcs ) |
David Turner | 5893c1b | 2001-10-18 11:49:26 +0000 | [diff] [blame] | 457 | for ( n = cur->n_points; n > 0; n--, vec++ ) |
| 458 | { |
| 459 | vec->x = FT_MulFix( vec->x, x_scale ); |
| 460 | vec->y = FT_MulFix( vec->y, y_scale ); |
| 461 | } |
David Turner | d8723b4 | 2000-04-14 11:29:14 +0000 | [diff] [blame] | 462 | |
David Turner | ed7f62a | 2000-03-28 11:19:28 +0000 | [diff] [blame] | 463 | /* Then scale the metrics */ |
Werner Lemberg | b66efef | 2009-03-12 08:07:49 +0000 | [diff] [blame] | 464 | metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); |
| 465 | metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); |
David Turner | ed7f62a | 2000-03-28 11:19:28 +0000 | [diff] [blame] | 466 | } |
David Turner | d8723b4 | 2000-04-14 11:29:14 +0000 | [diff] [blame] | 467 | |
| 468 | /* compute the other metrics */ |
| 469 | FT_Outline_Get_CBox( &glyph->root.outline, &cbox ); |
David Turner | e49ab25 | 2000-05-16 23:44:38 +0000 | [diff] [blame] | 470 | |
David Turner | d8723b4 | 2000-04-14 11:29:14 +0000 | [diff] [blame] | 471 | metrics->width = cbox.xMax - cbox.xMin; |
| 472 | metrics->height = cbox.yMax - cbox.yMin; |
| 473 | |
| 474 | metrics->horiBearingX = cbox.xMin; |
| 475 | metrics->horiBearingY = cbox.yMax; |
Wu, Chia-I (吳佳一) | 8cfb220 | 2005-06-20 09:04:50 +0000 | [diff] [blame] | 476 | |
Ken Sharp | 63e7aac | 2010-02-10 07:13:27 +0100 | [diff] [blame] | 477 | if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) |
| 478 | { |
| 479 | /* make up vertical ones */ |
| 480 | ft_synthesize_vertical_metrics( metrics, |
| 481 | metrics->vertAdvance ); |
| 482 | } |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 483 | } |
Werner Lemberg | 415235d | 2001-06-28 17:49:10 +0000 | [diff] [blame] | 484 | |
Werner Lemberg | e274cfd | 2001-03-04 21:53:08 +0000 | [diff] [blame] | 485 | /* Set control data to the glyph charstrings. Note that this is */ |
| 486 | /* _not_ zero-terminated. */ |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 487 | glyph->root.control_data = (FT_Byte*)glyph_data.pointer; |
| 488 | glyph->root.control_len = glyph_data.length; |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 489 | } |
Werner Lemberg | 8728f29 | 2000-08-23 17:32:42 +0000 | [diff] [blame] | 490 | |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 491 | |
David Turner | 9748807 | 2000-08-22 22:36:33 +0000 | [diff] [blame] | 492 | Exit: |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 493 | |
| 494 | #ifdef FT_CONFIG_OPTION_INCREMENTAL |
| 495 | if ( glyph_data_loaded && face->root.internal->incremental_interface ) |
| 496 | { |
| 497 | face->root.internal->incremental_interface->funcs->free_glyph_data( |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 498 | face->root.internal->incremental_interface->object, |
| 499 | &glyph_data ); |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 500 | |
| 501 | /* Set the control data to null - it is no longer available if */ |
Werner Lemberg | 3c403e4 | 2002-08-06 21:47:40 +0000 | [diff] [blame] | 502 | /* loaded incrementally. */ |
Graham Asher | ce12182 | 2002-08-01 15:29:17 +0000 | [diff] [blame] | 503 | glyph->root.control_data = 0; |
| 504 | glyph->root.control_len = 0; |
| 505 | } |
| 506 | #endif |
| 507 | |
Werner Lemberg | a4d6a1c | 2006-07-19 09:54:56 +0000 | [diff] [blame] | 508 | if ( must_finish_decoder ) |
| 509 | decoder_funcs->done( &decoder ); |
| 510 | |
David Turner | 2dfb5ac | 2000-01-27 14:02:04 +0000 | [diff] [blame] | 511 | return error; |
| 512 | } |
| 513 | |
Werner Lemberg | 7fa51b5 | 2000-07-08 19:51:42 +0000 | [diff] [blame] | 514 | |
| 515 | /* END */ |