Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3918 | Serge | 1 | /***************************************************************************/ |
2 | /* */ |
||
3 | /* t1objs.c */ |
||
4 | /* */ |
||
5 | /* Type 1 objects manager (body). */ |
||
6 | /* */ |
||
7 | /* Copyright 1996-2009, 2011, 2013 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 |
||
20 | #include FT_INTERNAL_CALC_H |
||
21 | #include FT_INTERNAL_DEBUG_H |
||
22 | #include FT_INTERNAL_STREAM_H |
||
23 | #include FT_TRUETYPE_IDS_H |
||
24 | |||
25 | #include "t1gload.h" |
||
26 | #include "t1load.h" |
||
27 | |||
28 | #include "t1errors.h" |
||
29 | |||
30 | #ifndef T1_CONFIG_OPTION_NO_AFM |
||
31 | #include "t1afm.h" |
||
32 | #endif |
||
33 | |||
34 | #include FT_SERVICE_POSTSCRIPT_CMAPS_H |
||
35 | #include FT_INTERNAL_POSTSCRIPT_AUX_H |
||
36 | |||
37 | |||
38 | /*************************************************************************/ |
||
39 | /* */ |
||
40 | /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
||
41 | /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ |
||
42 | /* messages during execution. */ |
||
43 | /* */ |
||
44 | #undef FT_COMPONENT |
||
45 | #define FT_COMPONENT trace_t1objs |
||
46 | |||
47 | |||
48 | /*************************************************************************/ |
||
49 | /* */ |
||
50 | /* SIZE FUNCTIONS */ |
||
51 | /* */ |
||
52 | /* note that we store the global hints in the size's "internal" root */ |
||
53 | /* field */ |
||
54 | /* */ |
||
55 | /*************************************************************************/ |
||
56 | |||
57 | |||
58 | static PSH_Globals_Funcs |
||
59 | T1_Size_Get_Globals_Funcs( T1_Size size ) |
||
60 | { |
||
61 | T1_Face face = (T1_Face)size->root.face; |
||
62 | PSHinter_Service pshinter = (PSHinter_Service)face->pshinter; |
||
63 | FT_Module module; |
||
64 | |||
65 | |||
66 | module = FT_Get_Module( size->root.face->driver->root.library, |
||
67 | "pshinter" ); |
||
68 | return ( module && pshinter && pshinter->get_globals_funcs ) |
||
69 | ? pshinter->get_globals_funcs( module ) |
||
70 | : 0 ; |
||
71 | } |
||
72 | |||
73 | |||
74 | FT_LOCAL_DEF( void ) |
||
75 | T1_Size_Done( FT_Size t1size ) /* T1_Size */ |
||
76 | { |
||
77 | T1_Size size = (T1_Size)t1size; |
||
78 | |||
79 | |||
80 | if ( size->root.internal ) |
||
81 | { |
||
82 | PSH_Globals_Funcs funcs; |
||
83 | |||
84 | |||
85 | funcs = T1_Size_Get_Globals_Funcs( size ); |
||
86 | if ( funcs ) |
||
87 | funcs->destroy( (PSH_Globals)size->root.internal ); |
||
88 | |||
89 | size->root.internal = 0; |
||
90 | } |
||
91 | } |
||
92 | |||
93 | |||
94 | FT_LOCAL_DEF( FT_Error ) |
||
95 | T1_Size_Init( FT_Size t1size ) /* T1_Size */ |
||
96 | { |
||
97 | T1_Size size = (T1_Size)t1size; |
||
98 | FT_Error error = FT_Err_Ok; |
||
99 | PSH_Globals_Funcs funcs = T1_Size_Get_Globals_Funcs( size ); |
||
100 | |||
101 | |||
102 | if ( funcs ) |
||
103 | { |
||
104 | PSH_Globals globals; |
||
105 | T1_Face face = (T1_Face)size->root.face; |
||
106 | |||
107 | |||
108 | error = funcs->create( size->root.face->memory, |
||
109 | &face->type1.private_dict, &globals ); |
||
110 | if ( !error ) |
||
111 | size->root.internal = (FT_Size_Internal)(void*)globals; |
||
112 | } |
||
113 | |||
114 | return error; |
||
115 | } |
||
116 | |||
117 | |||
118 | FT_LOCAL_DEF( FT_Error ) |
||
119 | T1_Size_Request( FT_Size t1size, /* T1_Size */ |
||
120 | FT_Size_Request req ) |
||
121 | { |
||
122 | T1_Size size = (T1_Size)t1size; |
||
123 | PSH_Globals_Funcs funcs = T1_Size_Get_Globals_Funcs( size ); |
||
124 | |||
125 | |||
126 | FT_Request_Metrics( size->root.face, req ); |
||
127 | |||
128 | if ( funcs ) |
||
129 | funcs->set_scale( (PSH_Globals)size->root.internal, |
||
130 | size->root.metrics.x_scale, |
||
131 | size->root.metrics.y_scale, |
||
132 | 0, 0 ); |
||
133 | |||
134 | return FT_Err_Ok; |
||
135 | } |
||
136 | |||
137 | |||
138 | /*************************************************************************/ |
||
139 | /* */ |
||
140 | /* SLOT FUNCTIONS */ |
||
141 | /* */ |
||
142 | /*************************************************************************/ |
||
143 | |||
144 | FT_LOCAL_DEF( void ) |
||
145 | T1_GlyphSlot_Done( FT_GlyphSlot slot ) |
||
146 | { |
||
147 | slot->internal->glyph_hints = 0; |
||
148 | } |
||
149 | |||
150 | |||
151 | FT_LOCAL_DEF( FT_Error ) |
||
152 | T1_GlyphSlot_Init( FT_GlyphSlot slot ) |
||
153 | { |
||
154 | T1_Face face; |
||
155 | PSHinter_Service pshinter; |
||
156 | |||
157 | |||
158 | face = (T1_Face)slot->face; |
||
159 | pshinter = (PSHinter_Service)face->pshinter; |
||
160 | |||
161 | if ( pshinter ) |
||
162 | { |
||
163 | FT_Module module; |
||
164 | |||
165 | |||
166 | module = FT_Get_Module( slot->face->driver->root.library, |
||
167 | "pshinter" ); |
||
168 | if ( module ) |
||
169 | { |
||
170 | T1_Hints_Funcs funcs; |
||
171 | |||
172 | |||
173 | funcs = pshinter->get_t1_funcs( module ); |
||
174 | slot->internal->glyph_hints = (void*)funcs; |
||
175 | } |
||
176 | } |
||
177 | |||
178 | return 0; |
||
179 | } |
||
180 | |||
181 | |||
182 | /*************************************************************************/ |
||
183 | /* */ |
||
184 | /* FACE FUNCTIONS */ |
||
185 | /* */ |
||
186 | /*************************************************************************/ |
||
187 | |||
188 | |||
189 | /*************************************************************************/ |
||
190 | /* */ |
||
191 | /* |
||
192 | /* T1_Face_Done */ |
||
193 | /* */ |
||
194 | /* |
||
195 | /* The face object destructor. */ |
||
196 | /* */ |
||
197 | /* */ |
||
198 | /* face :: A typeless pointer to the face object to destroy. */ |
||
199 | /* */ |
||
200 | FT_LOCAL_DEF( void ) |
||
201 | T1_Face_Done( FT_Face t1face ) /* T1_Face */ |
||
202 | { |
||
203 | T1_Face face = (T1_Face)t1face; |
||
204 | FT_Memory memory; |
||
205 | T1_Font type1; |
||
206 | |||
207 | |||
208 | if ( !face ) |
||
209 | return; |
||
210 | |||
211 | memory = face->root.memory; |
||
212 | type1 = &face->type1; |
||
213 | |||
214 | #ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT |
||
215 | /* release multiple masters information */ |
||
216 | FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) ); |
||
217 | |||
218 | if ( face->buildchar ) |
||
219 | { |
||
220 | FT_FREE( face->buildchar ); |
||
221 | |||
222 | face->buildchar = NULL; |
||
223 | face->len_buildchar = 0; |
||
224 | } |
||
225 | |||
226 | T1_Done_Blend( face ); |
||
227 | face->blend = 0; |
||
228 | #endif |
||
229 | |||
230 | /* release font info strings */ |
||
231 | { |
||
232 | PS_FontInfo info = &type1->font_info; |
||
233 | |||
234 | |||
235 | FT_FREE( info->version ); |
||
236 | FT_FREE( info->notice ); |
||
237 | FT_FREE( info->full_name ); |
||
238 | FT_FREE( info->family_name ); |
||
239 | FT_FREE( info->weight ); |
||
240 | } |
||
241 | |||
242 | /* release top dictionary */ |
||
243 | FT_FREE( type1->charstrings_len ); |
||
244 | FT_FREE( type1->charstrings ); |
||
245 | FT_FREE( type1->glyph_names ); |
||
246 | |||
247 | FT_FREE( type1->subrs ); |
||
248 | FT_FREE( type1->subrs_len ); |
||
249 | |||
250 | FT_FREE( type1->subrs_block ); |
||
251 | FT_FREE( type1->charstrings_block ); |
||
252 | FT_FREE( type1->glyph_names_block ); |
||
253 | |||
254 | FT_FREE( type1->encoding.char_index ); |
||
255 | FT_FREE( type1->encoding.char_name ); |
||
256 | FT_FREE( type1->font_name ); |
||
257 | |||
258 | #ifndef T1_CONFIG_OPTION_NO_AFM |
||
259 | /* release afm data if present */ |
||
260 | if ( face->afm_data ) |
||
261 | T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data ); |
||
262 | #endif |
||
263 | |||
264 | /* release unicode map, if any */ |
||
265 | #if 0 |
||
266 | FT_FREE( face->unicode_map_rec.maps ); |
||
267 | face->unicode_map_rec.num_maps = 0; |
||
268 | face->unicode_map = NULL; |
||
269 | #endif |
||
270 | |||
271 | face->root.family_name = NULL; |
||
272 | face->root.style_name = NULL; |
||
273 | } |
||
274 | |||
275 | |||
276 | /*************************************************************************/ |
||
277 | /* */ |
||
278 | /* |
||
279 | /* T1_Face_Init */ |
||
280 | /* */ |
||
281 | /* |
||
282 | /* The face object constructor. */ |
||
283 | /* */ |
||
284 | /* */ |
||
285 | /* stream :: input stream where to load font data. */ |
||
286 | /* */ |
||
287 | /* face_index :: The index of the font face in the resource. */ |
||
288 | /* */ |
||
289 | /* num_params :: Number of additional generic parameters. Ignored. */ |
||
290 | /* */ |
||
291 | /* params :: Additional generic parameters. Ignored. */ |
||
292 | /* */ |
||
293 | /* |
||
294 | /* face :: The face record to build. */ |
||
295 | /* */ |
||
296 | /* |
||
297 | /* FreeType error code. 0 means success. */ |
||
298 | /* */ |
||
299 | FT_LOCAL_DEF( FT_Error ) |
||
300 | T1_Face_Init( FT_Stream stream, |
||
301 | FT_Face t1face, /* T1_Face */ |
||
302 | FT_Int face_index, |
||
303 | FT_Int num_params, |
||
304 | FT_Parameter* params ) |
||
305 | { |
||
306 | T1_Face face = (T1_Face)t1face; |
||
307 | FT_Error error; |
||
308 | FT_Service_PsCMaps psnames; |
||
309 | PSAux_Service psaux; |
||
310 | T1_Font type1 = &face->type1; |
||
311 | PS_FontInfo info = &type1->font_info; |
||
312 | |||
313 | FT_UNUSED( num_params ); |
||
314 | FT_UNUSED( params ); |
||
315 | FT_UNUSED( stream ); |
||
316 | |||
317 | |||
318 | face->root.num_faces = 1; |
||
319 | |||
320 | FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS ); |
||
321 | face->psnames = psnames; |
||
322 | |||
323 | face->psaux = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ), |
||
324 | "psaux" ); |
||
325 | psaux = (PSAux_Service)face->psaux; |
||
326 | if ( !psaux ) |
||
327 | { |
||
328 | FT_ERROR(( "T1_Face_Init: cannot access `psaux' module\n" )); |
||
329 | error = FT_THROW( Missing_Module ); |
||
330 | goto Exit; |
||
331 | } |
||
332 | |||
333 | face->pshinter = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ), |
||
334 | "pshinter" ); |
||
335 | |||
336 | FT_TRACE2(( "Type 1 driver\n" )); |
||
337 | |||
338 | /* open the tokenizer; this will also check the font format */ |
||
339 | error = T1_Open_Face( face ); |
||
340 | if ( error ) |
||
341 | goto Exit; |
||
342 | |||
343 | /* if we just wanted to check the format, leave successfully now */ |
||
344 | if ( face_index < 0 ) |
||
345 | goto Exit; |
||
346 | |||
347 | /* check the face index */ |
||
348 | if ( face_index > 0 ) |
||
349 | { |
||
350 | FT_ERROR(( "T1_Face_Init: invalid face index\n" )); |
||
351 | error = FT_THROW( Invalid_Argument ); |
||
352 | goto Exit; |
||
353 | } |
||
354 | |||
355 | /* now load the font program into the face object */ |
||
356 | |||
357 | /* initialize the face object fields */ |
||
358 | |||
359 | /* set up root face fields */ |
||
360 | { |
||
361 | FT_Face root = (FT_Face)&face->root; |
||
362 | |||
363 | |||
364 | root->num_glyphs = type1->num_glyphs; |
||
365 | root->face_index = 0; |
||
366 | |||
367 | root->face_flags = FT_FACE_FLAG_SCALABLE | |
||
368 | FT_FACE_FLAG_HORIZONTAL | |
||
369 | FT_FACE_FLAG_GLYPH_NAMES | |
||
370 | FT_FACE_FLAG_HINTER; |
||
371 | |||
372 | if ( info->is_fixed_pitch ) |
||
373 | root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH; |
||
374 | |||
375 | if ( face->blend ) |
||
376 | root->face_flags |= FT_FACE_FLAG_MULTIPLE_MASTERS; |
||
377 | |||
378 | /* XXX: TODO -- add kerning with .afm support */ |
||
379 | |||
380 | |||
381 | /* The following code to extract the family and the style is very */ |
||
382 | /* simplistic and might get some things wrong. For a full-featured */ |
||
383 | /* algorithm you might have a look at the whitepaper given at */ |
||
384 | /* */ |
||
385 | /* http://blogs.msdn.com/text/archive/2007/04/23/wpf-font-selection-model.aspx */ |
||
386 | |||
387 | /* get style name -- be careful, some broken fonts only */ |
||
388 | /* have a `/FontName' dictionary entry! */ |
||
389 | root->family_name = info->family_name; |
||
390 | root->style_name = NULL; |
||
391 | |||
392 | if ( root->family_name ) |
||
393 | { |
||
394 | char* full = info->full_name; |
||
395 | char* family = root->family_name; |
||
396 | |||
397 | |||
398 | if ( full ) |
||
399 | { |
||
400 | FT_Bool the_same = TRUE; |
||
401 | |||
402 | |||
403 | while ( *full ) |
||
404 | { |
||
405 | if ( *full == *family ) |
||
406 | { |
||
407 | family++; |
||
408 | full++; |
||
409 | } |
||
410 | else |
||
411 | { |
||
412 | if ( *full == ' ' || *full == '-' ) |
||
413 | full++; |
||
414 | else if ( *family == ' ' || *family == '-' ) |
||
415 | family++; |
||
416 | else |
||
417 | { |
||
418 | the_same = FALSE; |
||
419 | |||
420 | if ( !*family ) |
||
421 | root->style_name = full; |
||
422 | break; |
||
423 | } |
||
424 | } |
||
425 | } |
||
426 | |||
427 | if ( the_same ) |
||
428 | root->style_name = (char *)"Regular"; |
||
429 | } |
||
430 | } |
||
431 | else |
||
432 | { |
||
433 | /* do we have a `/FontName'? */ |
||
434 | if ( type1->font_name ) |
||
435 | root->family_name = type1->font_name; |
||
436 | } |
||
437 | |||
438 | if ( !root->style_name ) |
||
439 | { |
||
440 | if ( info->weight ) |
||
441 | root->style_name = info->weight; |
||
442 | else |
||
443 | /* assume `Regular' style because we don't know better */ |
||
444 | root->style_name = (char *)"Regular"; |
||
445 | } |
||
446 | |||
447 | /* compute style flags */ |
||
448 | root->style_flags = 0; |
||
449 | if ( info->italic_angle ) |
||
450 | root->style_flags |= FT_STYLE_FLAG_ITALIC; |
||
451 | if ( info->weight ) |
||
452 | { |
||
453 | if ( !ft_strcmp( info->weight, "Bold" ) || |
||
454 | !ft_strcmp( info->weight, "Black" ) ) |
||
455 | root->style_flags |= FT_STYLE_FLAG_BOLD; |
||
456 | } |
||
457 | |||
458 | /* no embedded bitmap support */ |
||
459 | root->num_fixed_sizes = 0; |
||
460 | root->available_sizes = 0; |
||
461 | |||
462 | root->bbox.xMin = type1->font_bbox.xMin >> 16; |
||
463 | root->bbox.yMin = type1->font_bbox.yMin >> 16; |
||
464 | /* no `U' suffix here to 0xFFFF! */ |
||
465 | root->bbox.xMax = ( type1->font_bbox.xMax + 0xFFFF ) >> 16; |
||
466 | root->bbox.yMax = ( type1->font_bbox.yMax + 0xFFFF ) >> 16; |
||
467 | |||
468 | /* Set units_per_EM if we didn't set it in t1_parse_font_matrix. */ |
||
469 | if ( !root->units_per_EM ) |
||
470 | root->units_per_EM = 1000; |
||
471 | |||
472 | root->ascender = (FT_Short)( root->bbox.yMax ); |
||
473 | root->descender = (FT_Short)( root->bbox.yMin ); |
||
474 | |||
475 | root->height = (FT_Short)( ( root->units_per_EM * 12 ) / 10 ); |
||
476 | if ( root->height < root->ascender - root->descender ) |
||
477 | root->height = (FT_Short)( root->ascender - root->descender ); |
||
478 | |||
479 | /* now compute the maximum advance width */ |
||
480 | root->max_advance_width = |
||
481 | (FT_Short)( root->bbox.xMax ); |
||
482 | { |
||
483 | FT_Pos max_advance; |
||
484 | |||
485 | |||
486 | error = T1_Compute_Max_Advance( face, &max_advance ); |
||
487 | |||
488 | /* in case of error, keep the standard width */ |
||
489 | if ( !error ) |
||
490 | root->max_advance_width = (FT_Short)FIXED_TO_INT( max_advance ); |
||
491 | else |
||
492 | error = FT_Err_Ok; /* clear error */ |
||
493 | } |
||
494 | |||
495 | root->max_advance_height = root->height; |
||
496 | |||
497 | root->underline_position = (FT_Short)info->underline_position; |
||
498 | root->underline_thickness = (FT_Short)info->underline_thickness; |
||
499 | } |
||
500 | |||
501 | { |
||
502 | FT_Face root = &face->root; |
||
503 | |||
504 | |||
505 | if ( psnames ) |
||
506 | { |
||
507 | FT_CharMapRec charmap; |
||
508 | T1_CMap_Classes cmap_classes = psaux->t1_cmap_classes; |
||
509 | FT_CMap_Class clazz; |
||
510 | |||
511 | |||
512 | charmap.face = root; |
||
513 | |||
514 | /* first of all, try to synthesize a Unicode charmap */ |
||
515 | charmap.platform_id = TT_PLATFORM_MICROSOFT; |
||
516 | charmap.encoding_id = TT_MS_ID_UNICODE_CS; |
||
517 | charmap.encoding = FT_ENCODING_UNICODE; |
||
518 | |||
519 | error = FT_CMap_New( cmap_classes->unicode, NULL, &charmap, NULL ); |
||
520 | if ( error && |
||
521 | FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) ) |
||
522 | goto Exit; |
||
523 | error = FT_Err_Ok; |
||
524 | |||
525 | /* now, generate an Adobe Standard encoding when appropriate */ |
||
526 | charmap.platform_id = TT_PLATFORM_ADOBE; |
||
527 | clazz = NULL; |
||
528 | |||
529 | switch ( type1->encoding_type ) |
||
530 | { |
||
531 | case T1_ENCODING_TYPE_STANDARD: |
||
532 | charmap.encoding = FT_ENCODING_ADOBE_STANDARD; |
||
533 | charmap.encoding_id = TT_ADOBE_ID_STANDARD; |
||
534 | clazz = cmap_classes->standard; |
||
535 | break; |
||
536 | |||
537 | case T1_ENCODING_TYPE_EXPERT: |
||
538 | charmap.encoding = FT_ENCODING_ADOBE_EXPERT; |
||
539 | charmap.encoding_id = TT_ADOBE_ID_EXPERT; |
||
540 | clazz = cmap_classes->expert; |
||
541 | break; |
||
542 | |||
543 | case T1_ENCODING_TYPE_ARRAY: |
||
544 | charmap.encoding = FT_ENCODING_ADOBE_CUSTOM; |
||
545 | charmap.encoding_id = TT_ADOBE_ID_CUSTOM; |
||
546 | clazz = cmap_classes->custom; |
||
547 | break; |
||
548 | |||
549 | case T1_ENCODING_TYPE_ISOLATIN1: |
||
550 | charmap.encoding = FT_ENCODING_ADOBE_LATIN_1; |
||
551 | charmap.encoding_id = TT_ADOBE_ID_LATIN_1; |
||
552 | clazz = cmap_classes->unicode; |
||
553 | break; |
||
554 | |||
555 | default: |
||
556 | ; |
||
557 | } |
||
558 | |||
559 | if ( clazz ) |
||
560 | error = FT_CMap_New( clazz, NULL, &charmap, NULL ); |
||
561 | |||
562 | #if 0 |
||
563 | /* Select default charmap */ |
||
564 | if (root->num_charmaps) |
||
565 | root->charmap = root->charmaps[0]; |
||
566 | #endif |
||
567 | } |
||
568 | } |
||
569 | |||
570 | Exit: |
||
571 | return error; |
||
572 | } |
||
573 | |||
574 | |||
575 | /*************************************************************************/ |
||
576 | /* */ |
||
577 | /* |
||
578 | /* T1_Driver_Init */ |
||
579 | /* */ |
||
580 | /* |
||
581 | /* Initializes a given Type 1 driver object. */ |
||
582 | /* */ |
||
583 | /* */ |
||
584 | /* driver :: A handle to the target driver object. */ |
||
585 | /* */ |
||
586 | /* |
||
587 | /* FreeType error code. 0 means success. */ |
||
588 | /* */ |
||
589 | FT_LOCAL_DEF( FT_Error ) |
||
590 | T1_Driver_Init( FT_Module driver ) |
||
591 | { |
||
592 | FT_UNUSED( driver ); |
||
593 | |||
594 | return FT_Err_Ok; |
||
595 | } |
||
596 | |||
597 | |||
598 | /*************************************************************************/ |
||
599 | /* */ |
||
600 | /* |
||
601 | /* T1_Driver_Done */ |
||
602 | /* */ |
||
603 | /* |
||
604 | /* Finalizes a given Type 1 driver. */ |
||
605 | /* */ |
||
606 | /* */ |
||
607 | /* driver :: A handle to the target Type 1 driver. */ |
||
608 | /* */ |
||
609 | FT_LOCAL_DEF( void ) |
||
610 | T1_Driver_Done( FT_Module driver ) |
||
611 | { |
||
612 | FT_UNUSED( driver ); |
||
613 | } |
||
614 | |||
615 | |||
616 | /* END */>> |