Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1997, 1998, 1999, 2000 Aladdin Enterprises.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
/* $Id: gxtype1.c,v 1.41 2004/11/19 04:39:11 ray Exp $ */
18
/* Adobe Type 1 font interpreter support */
19
#include "math_.h"
20
#include "memory_.h"
21
#include "gx.h"
22
#include "gserrors.h"
23
#include "gsccode.h"
24
#include "gsline.h"
25
#include "gsstruct.h"
26
#include "gxarith.h"
27
#include "gxchrout.h"
28
#include "gxfixed.h"
29
#include "gxistate.h"
30
#include "gxmatrix.h"
31
#include "gxcoord.h"
32
#include "gxfont.h"
33
#include "gxfont1.h"
34
#include "gxtype1.h"
35
#include "gzpath.h"
36
 
37
/*
38
 * The routines in this file are used for both Type 1 and Type 2
39
 * charstring interpreters.
40
 */
41
 
42
/*
43
 * Define whether or not to force hints to "big pixel" boundaries
44
 * when rasterizing at higher resolution.  With the current algorithms,
45
 * a value of 1 is better for devices without alpha capability,
46
 * but 0 is better if alpha is available.
47
 */
48
#define FORCE_HINTS_TO_BIG_PIXELS 1
49
 
50
/* Structure descriptor */
51
public_st_gs_font_type1();
52
 
53
/* Define the structure type for a Type 1 interpreter state. */
54
public_st_gs_type1_state();
55
/* GC procedures */
56
private 
57
ENUM_PTRS_WITH(gs_type1_state_enum_ptrs, gs_type1_state *pcis)
58
{
59
    index -= 4;
60
    if (index < pcis->ips_count * ST_GLYPH_DATA_NUM_PTRS)
61
	return ENUM_USING(st_glyph_data,
62
			&pcis->ipstack[index / ST_GLYPH_DATA_NUM_PTRS].cs_data,
63
			  sizeof(pcis->ipstack[0].cs_data),
64
			  index % ST_GLYPH_DATA_NUM_PTRS);
65
    return 0;
66
}
67
ENUM_PTR3(0, gs_type1_state, pfont, pis, path);
68
ENUM_PTR(3, gs_type1_state, callback_data);
69
ENUM_PTRS_END
70
private RELOC_PTRS_WITH(gs_type1_state_reloc_ptrs, gs_type1_state *pcis)
71
{
72
    int i;
73
 
74
    RELOC_PTR(gs_type1_state, pfont);
75
    RELOC_PTR(gs_type1_state, pis);
76
    RELOC_PTR(gs_type1_state, path);
77
    RELOC_PTR(gs_type1_state, callback_data);
78
    for (i = 0; i < pcis->ips_count; i++) {
79
	ip_state_t *ipsp = &pcis->ipstack[i];
80
	int diff = ipsp->ip - ipsp->cs_data.bits.data;
81
 
82
	RELOC_USING(st_glyph_data, &ipsp->cs_data, sizeof(ipsp->cs_data));
83
	ipsp->ip = ipsp->cs_data.bits.data + diff;
84
    }
85
} RELOC_PTRS_END
86
 
87
/* Define a string to interact with unique_name in lib/pdf_font.ps .
88
   The string is used to resolve glyph name conflict while
89
   converting PDF Widths into Metrics.
90
 */
91
const char gx_extendeg_glyph_name_separator[] = "~GS~";
92
 
93
 
94
/* ------ Interpreter services ------ */
95
 
96
#define s (*ps)
97
 
98
/* Initialize a Type 1 interpreter. */
99
/* The caller must supply a string to the first call of gs_type1_interpret. */
100
int
101
gs_type1_interp_init(register gs_type1_state * pcis, gs_imager_state * pis,
102
    gx_path * ppath, const gs_log2_scale_point * pscale, 
103
    const gs_log2_scale_point * psubpixels, bool no_grid_fitting,
104
		     int paint_type, gs_font_type1 * pfont)
105
{
106
    static const gs_log2_scale_point no_scale = {0, 0};
107
    const gs_log2_scale_point *plog2_scale =
108
	(FORCE_HINTS_TO_BIG_PIXELS && pscale != NULL ? pscale : &no_scale);
109
    const gs_log2_scale_point *plog2_subpixels =
110
	(FORCE_HINTS_TO_BIG_PIXELS ? (psubpixels != NULL ? psubpixels : plog2_scale) : &no_scale);
111
 
112
    if_debug0('1', "[1]gs_type1_interp_init\n");
113
    pcis->pfont = pfont;
114
    pcis->pis = pis;
115
    pcis->path = ppath;
116
    pcis->callback_data = pfont; /* default callback data */
117
    pcis->no_grid_fitting = no_grid_fitting;
118
    pcis->paint_type = paint_type;
119
    pcis->os_count = 0;
120
    pcis->ips_count = 1;
121
    pcis->ipstack[0].ip = 0;
122
    gs_glyph_data_from_null(&pcis->ipstack[0].cs_data);
123
    pcis->ignore_pops = 0;
124
    pcis->init_done = -1;
125
    pcis->sb_set = false;
126
    pcis->width_set = false;
127
    pcis->num_hints = 0;
128
    pcis->seac_accent = -1;
129
    pcis->log2_subpixels = *plog2_subpixels;
130
 
131
    /* Set the sampling scale. */
132
    set_pixel_scale(&pcis->scale.x, plog2_scale->x);
133
    set_pixel_scale(&pcis->scale.y, plog2_scale->y);
134
 
135
    return 0;
136
}
137
 
138
/* Set the push/pop callback data. */
139
void
140
gs_type1_set_callback_data(gs_type1_state *pcis, void *callback_data)
141
{
142
    pcis->callback_data = callback_data;
143
}
144
 
145
 
146
/* Preset the left side bearing and/or width. */
147
void
148
gs_type1_set_lsb(gs_type1_state * pcis, const gs_point * psbpt)
149
{
150
    pcis->lsb.x = float2fixed(psbpt->x);
151
    pcis->lsb.y = float2fixed(psbpt->y);
152
    pcis->sb_set = true;
153
}
154
void
155
gs_type1_set_width(gs_type1_state * pcis, const gs_point * pwpt)
156
{
157
    pcis->width.x = float2fixed(pwpt->x);
158
    pcis->width.y = float2fixed(pwpt->y);
159
    pcis->width_set = true;
160
}
161
 
162
/* Finish initializing the interpreter if we are actually rasterizing */
163
/* the character, as opposed to just computing the side bearing and width. */
164
void
165
gs_type1_finish_init(gs_type1_state * pcis)
166
{
167
    gs_imager_state *pis = pcis->pis;
168
    const int max_coeff_bits = 11;	/* max coefficient in char space */
169
 
170
    /* Set up the fixed version of the transformation. */
171
    gx_matrix_to_fixed_coeff(&ctm_only(pis), &pcis->fc, max_coeff_bits);
172
 
173
    /* Set the current point of the path to the origin, */
174
    pcis->origin.x = pcis->path->position.x;
175
    pcis->origin.y = pcis->path->position.y;
176
 
177
    /* Initialize hint-related scalars. */
178
    pcis->asb_diff = pcis->adxy.x = pcis->adxy.y = 0;
179
    pcis->flex_count = flex_max;	/* not in Flex */
180
    pcis->vs_offset.x = pcis->vs_offset.y = 0;
181
 
182
 
183
    /* Compute the flatness needed for accurate rendering. */
184
    pcis->flatness = gs_char_flatness(pis, 0.001);
185
 
186
    pcis->init_done = 1;
187
}
188
 
189
 
190
#undef s
191
 
192
/* Record the side bearing and character width. */
193
int
194
gs_type1_sbw(gs_type1_state * pcis, fixed lsbx, fixed lsby, fixed wx, fixed wy)
195
{
196
    if (!pcis->sb_set)
197
	pcis->lsb.x = lsbx, pcis->lsb.y = lsby,
198
	    pcis->sb_set = true;	/* needed for accented chars */
199
    if (!pcis->width_set)
200
	pcis->width.x = wx, pcis->width.y = wy,
201
	    pcis->width_set = true;
202
    if_debug4('1', "[1]sb=(%g,%g) w=(%g,%g)\n",
203
	      fixed2float(pcis->lsb.x), fixed2float(pcis->lsb.y),
204
	      fixed2float(pcis->width.x), fixed2float(pcis->width.y));
205
    return 0;
206
}
207
 
208
/* Blend values for a Multiple Master font instance. */
209
/* The stack holds values ... K*N othersubr#. */
210
int
211
gs_type1_blend(gs_type1_state *pcis, fixed *csp, int num_results)
212
{
213
    gs_type1_data *pdata = &pcis->pfont->data;
214
    int num_values = fixed2int_var(csp[-1]);
215
    int k1 = num_values / num_results - 1;
216
    int i, j;
217
    fixed *base;
218
    fixed *deltas;
219
 
220
    if (num_values < num_results ||
221
	num_values % num_results != 0
222
	)
223
	return_error(gs_error_invalidfont);
224
    base = csp - 1 - num_values;
225
    deltas = base + num_results - 1;
226
    for (j = 0; j < num_results;
227
	 j++, base++, deltas += k1
228
	 )
229
	for (i = 1; i <= k1; i++)
230
	    *base += (fixed)(deltas[i] *
231
		pdata->WeightVector.values[i]);
232
    pcis->ignore_pops = num_results;
233
    return num_values - num_results + 2;
234
}
235
 
236
/*
237
 * Handle a seac.  Do the base character now; when it finishes (detected
238
 * in endchar), do the accent.  Note that we pass only 4 operands on the
239
 * stack, and pass asb separately.
240
 */
241
int
242
gs_type1_seac(gs_type1_state * pcis, const fixed * cstack, fixed asb,
243
	      ip_state_t * ipsp)
244
{
245
    gs_font_type1 *pfont = pcis->pfont;
246
    gs_glyph_data_t bgdata;
247
    gs_const_string gstr;
248
    int code;
249
 
250
    /* Save away all the operands. */
251
    pcis->seac_accent = fixed2int_var(cstack[3]);
252
    pcis->save_asb = asb;
253
    pcis->save_lsb = pcis->lsb;
254
    pcis->save_adxy.x = cstack[0];
255
    pcis->save_adxy.y = cstack[1];
256
    pcis->os_count = 0;		/* clear */
257
    /* Ask the caller to provide the base character's CharString. */
258
    code = pfont->data.procs.seac_data
259
	(pfont, fixed2int_var(cstack[2]), NULL, &gstr, &bgdata);
260
    if (code < 0)
261
	return code;
262
    /* Continue with the supplied string. */
263
    ipsp->cs_data = bgdata;
264
    return 0;
265
}
266
 
267
/*
268
 * Handle the end of a character.  Return 0 if this is really the end of a
269
 * character, or 1 if we still have to process the accent of a seac.
270
 * In the latter case, the interpreter control stack has been set up to
271
 * point to the start of the accent's CharString; the caller must
272
 * also set ptx/y to pcis->position.x/y.
273
 */
274
int
275
gs_type1_endchar(gs_type1_state * pcis)
276
{
277
    gs_imager_state *pis = pcis->pis;
278
 
279
    if (pcis->seac_accent >= 0) {	/* We just finished the base character of a seac. */
280
	/* Do the accent. */
281
	gs_font_type1 *pfont = pcis->pfont;
282
	gs_glyph_data_t agdata;
283
	int achar = pcis->seac_accent;
284
	gs_const_string gstr;
285
	int code;
286
 
287
	agdata.memory = pfont->memory;
288
	pcis->seac_accent = -1;
289
	/* Reset the coordinate system origin */
290
	pcis->asb_diff = pcis->save_asb - pcis->save_lsb.x;
291
	pcis->adxy = pcis->save_adxy;
292
	pcis->os_count = 0;	/* clear */
293
	/* Clear the ipstack, in case the base character */
294
	/* ended inside a subroutine. */
295
	pcis->ips_count = 1;
296
	/* Ask the caller to provide the accent's CharString. */
297
	code = pfont->data.procs.seac_data(pfont, achar, NULL, &gstr, &agdata);
298
	if (code == gs_error_undefined) {
299
	    /* 
300
	     * The font is missing the accent's CharString (due to
301
	     * bad subsetting).  Just end drawing here without error. 
302
	     * This is like Acrobat Reader behaves.
303
	     */
304
	    char buf0[gs_font_name_max + 1], buf1[30];
305
	    int l0 = min(pcis->pfont->font_name.size, sizeof(buf0) - 1);
306
	    int l1 = min(gstr.size, sizeof(buf1) - 1);
307
 
308
	    memcpy(buf0, pcis->pfont->font_name.chars, l0);
309
	    buf0[l0] = 0;
310
	    memcpy(buf1, gstr.data, l1);
311
	    buf1[l1] = 0;
312
	    eprintf2("The font '%s' misses the glyph '%s' . Continue skipping the glyph.", buf0, buf1);
313
	    return 0;
314
	}
315
	if (code < 0)
316
	    return code;
317
	/* Continue with the supplied string. */
318
	pcis->ips_count = 1;
319
	pcis->ipstack[0].cs_data = agdata;
320
	return 1;
321
    }
322
    if (pcis->pfont->PaintType == 0)
323
	pis->fill_adjust.x = pis->fill_adjust.y = -1;
324
    /* Set the flatness for curve rendering. */
325
    if (!pcis->no_grid_fitting)
326
	gs_imager_setflat(pis, pcis->flatness);
327
    return 0;
328
}
329
 
330
/* Get the metrics (l.s.b. and width) from the Type 1 interpreter. */
331
void
332
type1_cis_get_metrics(const gs_type1_state * pcis, double psbw[4])
333
{
334
    psbw[0] = fixed2float(pcis->lsb.x);
335
    psbw[1] = fixed2float(pcis->lsb.y);
336
    psbw[2] = fixed2float(pcis->width.x);
337
    psbw[3] = fixed2float(pcis->width.y);
338
}
339
 
340
/* ------ Font procedures ------ */
341
 
342
 
343
/*
344
 * If a Type 1 character is defined with 'seac', store the character codes
345
 * in chars[0] and chars[1] and return 1; otherwise, return 0 or <0.
346
 * This is exported only for the benefit of font copying.
347
 */
348
int
349
gs_type1_piece_codes(/*const*/ gs_font_type1 *pfont,
350
		     const gs_glyph_data_t *pgd, gs_char *chars)
351
{
352
    gs_type1_data *const pdata = &pfont->data;
353
    /*
354
     * Decode the CharString looking for seac.  We have to process
355
     * callsubr, callothersubr, and return operators, but if we see
356
     * any other operators other than [h]sbw, pop, hint operators,
357
     * or endchar, we can return immediately.  We have to include
358
     * endchar because it is an (undocumented) equivalent for seac
359
     * in Type 2 CharStrings: see the cx_endchar case in
360
     * gs_type2_interpret in gstype2.c.
361
     *
362
     * It's really unfortunate that we have to duplicate so much parsing
363
     * code, but factoring out the parser from the interpreter would
364
     * involve more restructuring than we're prepared to do right now.
365
     */
366
    bool encrypted = pdata->lenIV >= 0;
367
    fixed cstack[ostack_size];
368
    fixed *csp;
369
    ip_state_t ipstack[ipstack_size + 1];
370
    ip_state_t *ipsp = &ipstack[0];
371
    const byte *cip;
372
    crypt_state state;
373
    int c;
374
    int code;
375
 
376
    CLEAR_CSTACK(cstack, csp);
377
    cip = pgd->bits.data;
378
 call:
379
    state = crypt_charstring_seed;
380
    if (encrypted) {
381
	int skip = pdata->lenIV;
382
 
383
	/* Skip initial random bytes */
384
	for (; skip > 0; ++cip, --skip)
385
	    decrypt_skip_next(*cip, state);
386
    }
387
 top:
388
    for (;;) {
389
	uint c0 = *cip++;
390
 
391
	charstring_next(c0, state, c, encrypted);
392
	if (c >= c_num1) {
393
	    /* This is a number, decode it and push it on the stack. */
394
	    if (c < c_pos2_0) {	/* 1-byte number */
395
		decode_push_num1(csp, cstack, c);
396
	    } else if (c < cx_num4) {	/* 2-byte number */
397
		decode_push_num2(csp, cstack, c, cip, state, encrypted);
398
	    } else if (c == cx_num4) {	/* 4-byte number */
399
		long lw;
400
 
401
		decode_num4(lw, cip, state, encrypted);
402
		CS_CHECK_PUSH(csp, cstack);
403
		*++csp = int2fixed(lw);
404
	    } else		/* not possible */
405
		return_error(gs_error_invalidfont);
406
	    continue;
407
	}
408
#define cnext CLEAR_CSTACK(cstack, csp); goto top
409
	switch ((char_command) c) {
410
	default:
411
	    goto out;
412
	case c_callsubr:
413
	    c = fixed2int_var(*csp) + pdata->subroutineNumberBias;
414
	    code = pdata->procs.subr_data
415
		(pfont, c, false, &ipsp[1].cs_data);
416
	    if (code < 0)
417
		return_error(code);
418
	    --csp;
419
	    ipsp->ip = cip, ipsp->dstate = state;
420
	    ++ipsp;
421
	    cip = ipsp->cs_data.bits.data;
422
	    goto call;
423
	case c_return:
424
	    gs_glyph_data_free(&ipsp->cs_data, "gs_type1_piece_codes");
425
	    --ipsp;
426
	    cip = ipsp->ip, state = ipsp->dstate;
427
	    goto top;
428
	case cx_hstem:
429
	case cx_vstem:
430
	case c1_hsbw:
431
	    cnext;
432
	case cx_endchar:
433
	    if (csp < cstack + 3)
434
		goto out;	/* not seac */
435
	do_seac:
436
	    /* This is the payoff for all this code! */
437
	    chars[0] = fixed2int(csp[-1]);
438
	    chars[1] = fixed2int(csp[0]);
439
	    return 1;
440
	case cx_escape:
441
	    charstring_next(*cip, state, c, encrypted);
442
	    ++cip;
443
	    switch ((char1_extended_command) c) {
444
	    default:
445
		goto out;
446
	    case ce1_vstem3:
447
	    case ce1_hstem3:
448
	    case ce1_sbw:
449
		cnext;
450
	    case ce1_pop:
451
		/*
452
		 * pop must do nothing, since it is used after
453
		 * subr# 1 3 callothersubr.
454
		 */
455
		goto top;
456
	    case ce1_seac:
457
		goto do_seac;
458
	    case ce1_callothersubr:
459
		switch (fixed2int_var(*csp)) {
460
		default:
461
		    goto out;
462
		case 3:
463
		    csp -= 2;
464
		    goto top;
465
		case 12:
466
		case 13:
467
		case 14:
468
		case 15:
469
		case 16:
470
		case 17:
471
		case 18:
472
		    cnext;
473
		}
474
	    }
475
	}
476
#undef cnext
477
    }
478
 out:
479
    return 0;
480
}
481
 
482
/*
483
 * Get PIECES and/or NUM_PIECES of a Type 1 glyph.  Sets info->num_pieces
484
 * and/or stores into info->pieces.  Updates info->members.  This is a
485
 * single-use procedure broken out only for readability.
486
 */
487
private int
488
gs_type1_glyph_pieces(gs_font_type1 *pfont, const gs_glyph_data_t *pgd,
489
		      int members, gs_glyph_info_t *info)
490
{
491
    gs_char chars[2];
492
    gs_glyph glyphs[2];
493
    int code = gs_type1_piece_codes(pfont, pgd, chars);
494
    gs_type1_data *const pdata = &pfont->data;
495
    gs_glyph *pieces =
496
	(members & GLYPH_INFO_PIECES ? info->pieces : glyphs);
497
    gs_const_string gstr;
498
    int acode, bcode;
499
 
500
    info->num_pieces = 0;	/* default */
501
    if (code <= 0)		/* no seac, possibly error */
502
	return code;
503
    bcode = pdata->procs.seac_data(pfont, chars[0], &pieces[0], &gstr, NULL);
504
    acode = pdata->procs.seac_data(pfont, chars[1], &pieces[1], &gstr, NULL);
505
    code = (bcode < 0 ? bcode : acode);
506
    info->num_pieces = 2;
507
    return code;
508
}
509
 
510
int
511
gs_type1_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
512
		    int members, gs_glyph_info_t *info)
513
{
514
    gs_font_type1 *const pfont = (gs_font_type1 *)font;
515
    gs_type1_data *const pdata = &pfont->data;
516
    int wmode = ((members & GLYPH_INFO_WIDTH1) != 0);
517
    int piece_members = members & (GLYPH_INFO_NUM_PIECES | GLYPH_INFO_PIECES);
518
    int width_members = (members & ((GLYPH_INFO_WIDTH0 << wmode) | (GLYPH_INFO_VVECTOR0 << wmode)));
519
    int default_members = members & ~(piece_members | GLYPH_INFO_WIDTHS |
520
				     GLYPH_INFO_VVECTOR0 | GLYPH_INFO_VVECTOR1 |
521
				     GLYPH_INFO_OUTLINE_WIDTHS);
522
    int code = 0;
523
    gs_glyph_data_t gdata;
524
 
525
    if (default_members) {
526
	code = gs_default_glyph_info(font, glyph, pmat, default_members, info);
527
 
528
	if (code < 0)
529
	    return code;
530
    } else
531
	info->members = 0;
532
 
533
    if (default_members == members)
534
	return code;		/* all done */
535
 
536
    gdata.memory = pfont->memory;
537
    if ((code = pdata->procs.glyph_data(pfont, glyph, &gdata)) < 0)
538
	return code;		/* non-existent glyph */
539
 
540
    if (piece_members) {
541
	code = gs_type1_glyph_pieces(pfont, &gdata, members, info);
542
	if (code < 0)
543
	    return code;
544
	info->members |= piece_members;
545
    }
546
 
547
    if (width_members) {
548
	/*
549
	 * Interpret the CharString until we get to the [h]sbw.
550
	 */
551
	gs_imager_state gis;
552
	gs_type1_state cis;
553
	int value;
554
 
555
	/* Initialize just enough of the imager state. */
556
	if (pmat)
557
	    gs_matrix_fixed_from_matrix(&gis.ctm, pmat);
558
	else {
559
	    gs_matrix imat;
560
 
561
	    gs_make_identity(&imat);
562
	    gs_matrix_fixed_from_matrix(&gis.ctm, &imat);
563
	}
564
	gis.flatness = 0;
565
	code = gs_type1_interp_init(&cis, &gis, NULL /* no path needed */,
566
				    NULL, NULL, true, 0, pfont);
567
	if (code < 0)
568
	    return code;
569
	cis.no_grid_fitting = true;
570
	code = pdata->interpret(&cis, &gdata, &value);
571
	switch (code) {
572
	case 0:		/* done with no [h]sbw, error */
573
	    code = gs_note_error(gs_error_invalidfont);
574
	default:		/* code < 0, error */
575
	    return code;
576
	case type1_result_callothersubr:	/* unknown OtherSubr */
577
	    return_error(gs_error_rangecheck); /* can't handle it */
578
	case type1_result_sbw:
579
	    info->width[wmode].x = fixed2float(cis.width.x);
580
	    info->width[wmode].y = fixed2float(cis.width.y);
581
	    info->v.x = fixed2float(cis.lsb.x);
582
	    info->v.y = fixed2float(cis.lsb.y);
583
	    break;
584
	}
585
	info->members |= width_members | (GLYPH_INFO_VVECTOR0 << wmode);
586
    }
587
 
588
    gs_glyph_data_free(&gdata, "gs_type1_glyph_info");
589
    return code;
590
}
591
 
592
/* Get font parent (a Type 9 font). */
593
const gs_font_base *
594
gs_font_parent(const gs_font_base *pbfont)
595
{
596
    if (pbfont->FontType == ft_encrypted || pbfont->FontType == ft_encrypted2) {
597
	const gs_font_type1 *pfont1 = (const gs_font_type1 *)pbfont;
598
 
599
	if (pfont1->data.parent != NULL)
600
	    return pfont1->data.parent;
601
    }
602
    return pbfont;
603
}