Subversion Repositories Applications.annuaire

Rev

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

Rev Author Line No. Line
296 aurelien 1
<?php
2
//============================================================+
3
// File name   : makefont.php
4
// Begin       : 2004-12-31
5
// Last Update : 2010-12-03
6
// Version     : 1.2.007
7
// License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
8
// 	----------------------------------------------------------------------------
9
// 	Copyright (C) 2008-2010  Nicola Asuni - Tecnick.com S.r.l.
10
//
11
// This file is part of TCPDF software library.
12
//
13
// TCPDF is free software: you can redistribute it and/or modify it
14
// under the terms of the GNU Lesser General Public License as
15
// published by the Free Software Foundation, either version 3 of the
16
// License, or (at your option) any later version.
17
//
18
// TCPDF is distributed in the hope that it will be useful, but
19
// WITHOUT ANY WARRANTY; without even the implied warranty of
20
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21
// See the GNU Lesser General Public License for more details.
22
//
23
// You should have received a copy of the GNU Lesser General Public License
24
// along with TCPDF.  If not, see <http://www.gnu.org/licenses/>.
25
//
26
// See LICENSE.TXT file for more information.
27
//  ----------------------------------------------------------------------------
28
//
29
// Description : Utility to generate font definition files fot TCPDF
30
//
31
// Authors: Nicola Asuni, Olivier Plathey, Steven Wittens
32
//
33
// (c) Copyright:
34
//               Nicola Asuni
35
//               Tecnick.com S.r.l.
36
//               Via della Pace, 11
37
//               09044 Quartucciu (CA)
38
//               ITALY
39
//               www.tecnick.com
40
//               info@tecnick.com
41
//============================================================+
42
 
43
/**
44
 * Utility to generate font definition files fot TCPDF.
45
 * @author Nicola Asuni, Olivier Plathey, Steven Wittens
46
 * @copyright 2004-2008 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
47
 * @package com.tecnick.tcpdf
48
 * @link http://www.tcpdf.org
49
 * @license http://www.gnu.org/copyleft/lesser.html LGPL
50
*/
51
 
52
/**
53
 *
54
 * @param string $fontfile path to font file (TTF, OTF or PFB).
55
 * @param string $fmfile font metrics file (UFM or AFM).
56
 * @param boolean $embedded Set to false to not embed the font, true otherwise (default).
57
 * @param string $enc Name of the encoding table to use. Omit this parameter for TrueType Unicode, OpenType Unicode and symbolic fonts like Symbol or ZapfDingBats.
58
 * @param array $patch Optional modification of the encoding
59
 */
60
function MakeFont($fontfile, $fmfile, $embedded=true, $enc='cp1252', $patch=array()) {
61
	//Generate a font definition file
62
	if(!defined('PHP_VERSION_ID')) {
63
		$version = PHP_VERSION;
64
		define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
65
	}
66
	if (PHP_VERSION_ID < 50300) {
67
		@set_magic_quotes_runtime(0);
68
	}
69
	ini_set('auto_detect_line_endings', '1');
70
	if (!file_exists($fontfile)) {
71
		die('Error: file not found: '.$fontfile);
72
	}
73
	if (!file_exists($fmfile)) {
74
		die('Error: file not found: '.$fmfile);
75
	}
76
	$cidtogidmap = '';
77
	$map = array();
78
	$diff = '';
79
	$dw = 0; // default width
80
	$ffext = strtolower(substr($fontfile, -3));
81
	$fmext = strtolower(substr($fmfile, -3));
82
	if ($fmext == 'afm') {
83
		if (($ffext == 'ttf') OR ($ffext == 'otf')) {
84
			$type = 'TrueType';
85
		} elseif ($ffext == 'pfb') {
86
			$type = 'Type1';
87
		} else {
88
			die('Error: unrecognized font file extension: '.$ffext);
89
		}
90
		if ($enc) {
91
			$map = ReadMap($enc);
92
			foreach ($patch as $cc => $gn) {
93
				$map[$cc] = $gn;
94
			}
95
		}
96
		$fm = ReadAFM($fmfile, $map);
97
		if (isset($widths['.notdef'])) {
98
			$dw = $widths['.notdef'];
99
		}
100
		if ($enc) {
101
			$diff = MakeFontEncoding($map);
102
		}
103
		$fd = MakeFontDescriptor($fm, empty($map));
104
	} elseif ($fmext == 'ufm') {
105
		$enc = '';
106
		if (($ffext == 'ttf') OR ($ffext == 'otf')) {
107
			$type = 'TrueTypeUnicode';
108
		} else {
109
			die('Error: not a TrueType font: '.$ffext);
110
		}
111
		$fm = ReadUFM($fmfile, $cidtogidmap);
112
		$dw = $fm['MissingWidth'];
113
		$fd = MakeFontDescriptor($fm, false);
114
	}
115
	//Start generation
116
	$s = '<?php'."\n";
117
	$s .= '$type=\''.$type."';\n";
118
	$s .= '$name=\''.$fm['FontName']."';\n";
119
	$s .= '$desc='.$fd.";\n";
120
	if (!isset($fm['UnderlinePosition'])) {
121
		$fm['UnderlinePosition'] = -100;
122
	}
123
	if (!isset($fm['UnderlineThickness'])) {
124
		$fm['UnderlineThickness'] = 50;
125
	}
126
	$s .= '$up='.$fm['UnderlinePosition'].";\n";
127
	$s .= '$ut='.$fm['UnderlineThickness'].";\n";
128
	if ($dw <= 0) {
129
		if (isset($fm['Widths'][32]) AND ($fm['Widths'][32] > 0)) {
130
			// assign default space width
131
			$dw = $fm['Widths'][32];
132
		} else {
133
			$dw = 600;
134
		}
135
	}
136
	$s .= '$dw='.$dw.";\n";
137
	$w = MakeWidthArray($fm);
138
	$s .= '$cw='.$w.";\n";
139
	$s .= '$enc=\''.$enc."';\n";
140
	$s .= '$diff=\''.$diff."';\n";
141
	$basename = substr(basename($fmfile), 0, -4);
142
	if ($embedded) {
143
		//Embedded font
144
		if (($type == 'TrueType') OR ($type == 'TrueTypeUnicode')) {
145
			CheckTTF($fontfile);
146
		}
147
		$f = fopen($fontfile,'rb');
148
		if (!$f) {
149
			die('Error: Unable to open '.$fontfile);
150
		}
151
		$file = fread($f, filesize($fontfile));
152
		fclose($f);
153
		if ($type == 'Type1') {
154
			//Find first two sections and discard third one
155
			$header = (ord($file{0}) == 128);
156
			if ($header) {
157
				//Strip first binary header
158
				$file = substr($file, 6);
159
			}
160
			$pos = strpos($file, 'eexec');
161
			if (!$pos) {
162
				die('Error: font file does not seem to be valid Type1');
163
			}
164
			$size1 = $pos + 6;
165
			if ($header AND (ord($file{$size1}) == 128)) {
166
				//Strip second binary header
167
				$file = substr($file, 0, $size1).substr($file, $size1+6);
168
			}
169
			$pos = strpos($file, '00000000');
170
			if (!$pos) {
171
				die('Error: font file does not seem to be valid Type1');
172
			}
173
			$size2 = $pos - $size1;
174
			$file = substr($file, 0, ($size1 + $size2));
175
		}
176
		$basename = strtolower($basename);
177
		if (function_exists('gzcompress')) {
178
			$cmp = $basename.'.z';
179
			SaveToFile($cmp, gzcompress($file, 9), 'b');
180
			$s .= '$file=\''.$cmp."';\n";
181
			print "Font file compressed (".$cmp.")\n";
182
			if (!empty($cidtogidmap)) {
183
				$cmp = $basename.'.ctg.z';
184
				SaveToFile($cmp, gzcompress($cidtogidmap, 9), 'b');
185
				print "CIDToGIDMap created and compressed (".$cmp.")\n";
186
				$s .= '$ctg=\''.$cmp."';\n";
187
			}
188
		} else {
189
			$s .= '$file=\''.basename($fontfile)."';\n";
190
			print "Notice: font file could not be compressed (zlib extension not available)\n";
191
			if (!empty($cidtogidmap)) {
192
				$cmp = $basename.'.ctg';
193
				$f = fopen($cmp, 'wb');
194
				fwrite($f, $cidtogidmap);
195
				fclose($f);
196
				print "CIDToGIDMap created (".$cmp.")\n";
197
				$s .= '$ctg=\''.$cmp."';\n";
198
			}
199
		}
200
		if($type == 'Type1') {
201
			$s .= '$size1='.$size1.";\n";
202
			$s .= '$size2='.$size2.";\n";
203
		} else {
204
			$s.='$originalsize='.filesize($fontfile).";\n";
205
		}
206
	} else {
207
		//Not embedded font
208
		$s .= '$file='."'';\n";
209
	}
210
	$s .= '// --- EOF ---';
211
	SaveToFile($basename.'.php',$s);
212
	print "Font definition file generated (".$basename.".php)\n";
213
}
214
 
215
/**
216
 * Read the specified encoding map.
217
 * @param string $enc map name (see /enc/ folder for valid names).
218
 */
219
function ReadMap($enc) {
220
	//Read a map file
221
	$file = dirname(__FILE__).'/enc/'.strtolower($enc).'.map';
222
	$a = file($file);
223
	if (empty($a)) {
224
		die('Error: encoding not found: '.$enc);
225
	}
226
	$cc2gn = array();
227
	foreach ($a as $l) {
228
		if ($l{0} == '!') {
229
			$e = preg_split('/[ \\t]+/',rtrim($l));
230
			$cc = hexdec(substr($e[0],1));
231
			$gn = $e[2];
232
			$cc2gn[$cc] = $gn;
233
		}
234
	}
235
	for($i = 0; $i <= 255; $i++) {
236
		if(!isset($cc2gn[$i])) {
237
			$cc2gn[$i] = '.notdef';
238
		}
239
	}
240
	return $cc2gn;
241
}
242
 
243
/**
244
 * Read UFM file
245
 */
246
function ReadUFM($file, &$cidtogidmap) {
247
	//Prepare empty CIDToGIDMap
248
	$cidtogidmap = str_pad('', (256 * 256 * 2), "\x00");
249
	//Read a font metric file
250
	$a = file($file);
251
	if (empty($a)) {
252
		die('File not found');
253
	}
254
	$widths = array();
255
	$fm = array();
256
	foreach($a as $l) {
257
		$e = explode(' ',chop($l));
258
		if(count($e) < 2) {
259
			continue;
260
		}
261
		$code = $e[0];
262
		$param = $e[1];
263
		if($code == 'U') {
264
			// U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
265
			//Character metrics
266
			$cc = (int)$e[1];
267
			if ($cc != -1) {
268
			$gn = $e[7];
269
			$w = $e[4];
270
			$glyph = $e[10];
271
			$widths[$cc] = $w;
272
			if($cc == ord('X')) {
273
				$fm['CapXHeight'] = $e[13];
274
			}
275
			// Set GID
276
			if (($cc >= 0) AND ($cc < 0xFFFF) AND $glyph) {
277
				$cidtogidmap{($cc * 2)} = chr($glyph >> 8);
278
				$cidtogidmap{(($cc * 2) + 1)} = chr($glyph & 0xFF);
279
			}
280
		}
281
		if((isset($gn) AND ($gn == '.notdef')) AND (!isset($fm['MissingWidth']))) {
282
			$fm['MissingWidth'] = $w;
283
		}
284
		} elseif($code == 'FontName') {
285
			$fm['FontName'] = $param;
286
		} elseif($code == 'Weight') {
287
			$fm['Weight'] = $param;
288
		} elseif($code == 'ItalicAngle') {
289
			$fm['ItalicAngle'] = (double)$param;
290
		} elseif($code == 'Ascender') {
291
			$fm['Ascender'] = (int)$param;
292
		} elseif($code == 'Descender') {
293
			$fm['Descender'] = (int)$param;
294
		} elseif($code == 'UnderlineThickness') {
295
			$fm['UnderlineThickness'] = (int)$param;
296
		} elseif($code == 'UnderlinePosition') {
297
			$fm['UnderlinePosition'] = (int)$param;
298
		} elseif($code == 'IsFixedPitch') {
299
			$fm['IsFixedPitch'] = ($param == 'true');
300
		} elseif($code == 'FontBBox') {
301
			$fm['FontBBox'] = array($e[1], $e[2], $e[3], $e[4]);
302
		} elseif($code == 'CapHeight') {
303
			$fm['CapHeight'] = (int)$param;
304
		} elseif($code == 'StdVW') {
305
			$fm['StdVW'] = (int)$param;
306
		}
307
	}
308
	if(!isset($fm['MissingWidth'])) {
309
		$fm['MissingWidth'] = 600;
310
	}
311
	if(!isset($fm['FontName'])) {
312
		die('FontName not found');
313
	}
314
	$fm['Widths'] = $widths;
315
	return $fm;
316
}
317
 
318
/**
319
 * Read AFM file
320
 */
321
function ReadAFM($file,&$map) {
322
	//Read a font metric file
323
	$a = file($file);
324
	if(empty($a)) {
325
		die('File not found');
326
	}
327
	$widths = array();
328
	$fm = array();
329
	$fix = array(
330
		'Edot'=>'Edotaccent',
331
		'edot'=>'edotaccent',
332
		'Idot'=>'Idotaccent',
333
		'Zdot'=>'Zdotaccent',
334
		'zdot'=>'zdotaccent',
335
		'Odblacute' => 'Ohungarumlaut',
336
		'odblacute' => 'ohungarumlaut',
337
		'Udblacute'=>'Uhungarumlaut',
338
		'udblacute'=>'uhungarumlaut',
339
		'Gcedilla'=>'Gcommaaccent'
340
		,'gcedilla'=>'gcommaaccent',
341
		'Kcedilla'=>'Kcommaaccent',
342
		'kcedilla'=>'kcommaaccent',
343
		'Lcedilla'=>'Lcommaaccent',
344
		'lcedilla'=>'lcommaaccent',
345
		'Ncedilla'=>'Ncommaaccent',
346
		'ncedilla'=>'ncommaaccent',
347
		'Rcedilla'=>'Rcommaaccent',
348
		'rcedilla'=>'rcommaaccent',
349
		'Scedilla'=>'Scommaaccent',
350
		'scedilla'=>'scommaaccent',
351
		'Tcedilla'=>'Tcommaaccent',
352
		'tcedilla'=>'tcommaaccent',
353
		'Dslash'=>'Dcroat',
354
		'dslash'=>'dcroat',
355
		'Dmacron'=>'Dcroat',
356
		'dmacron'=>'dcroat',
357
		'combininggraveaccent'=>'gravecomb',
358
		'combininghookabove'=>'hookabovecomb',
359
		'combiningtildeaccent'=>'tildecomb',
360
		'combiningacuteaccent'=>'acutecomb',
361
		'combiningdotbelow'=>'dotbelowcomb',
362
		'dongsign'=>'dong'
363
		);
364
	foreach($a as $l) {
365
		$e = explode(' ', rtrim($l));
366
		if (count($e) < 2) {
367
			continue;
368
		}
369
		$code = $e[0];
370
		$param = $e[1];
371
		if ($code == 'C') {
372
			//Character metrics
373
			$cc = (int)$e[1];
374
			$w = $e[4];
375
			$gn = $e[7];
376
			if (substr($gn, -4) == '20AC') {
377
				$gn = 'Euro';
378
			}
379
			if (isset($fix[$gn])) {
380
				//Fix incorrect glyph name
381
				foreach ($map as $c => $n) {
382
					if ($n == $fix[$gn]) {
383
						$map[$c] = $gn;
384
					}
385
				}
386
			}
387
			if (empty($map)) {
388
				//Symbolic font: use built-in encoding
389
				$widths[$cc] = $w;
390
			} else {
391
				$widths[$gn] = $w;
392
				if($gn == 'X') {
393
					$fm['CapXHeight'] = $e[13];
394
				}
395
			}
396
			if($gn == '.notdef') {
397
				$fm['MissingWidth'] = $w;
398
			}
399
		} elseif($code == 'FontName') {
400
			$fm['FontName'] = $param;
401
		} elseif($code == 'Weight') {
402
			$fm['Weight'] = $param;
403
		} elseif($code == 'ItalicAngle') {
404
			$fm['ItalicAngle'] = (double)$param;
405
		} elseif($code == 'Ascender') {
406
			$fm['Ascender'] = (int)$param;
407
		} elseif($code == 'Descender') {
408
			$fm['Descender'] = (int)$param;
409
		} elseif($code == 'UnderlineThickness') {
410
			$fm['UnderlineThickness'] = (int)$param;
411
		} elseif($code == 'UnderlinePosition') {
412
			$fm['UnderlinePosition'] = (int)$param;
413
		} elseif($code == 'IsFixedPitch') {
414
			$fm['IsFixedPitch'] = ($param == 'true');
415
		} elseif($code == 'FontBBox') {
416
			$fm['FontBBox'] = array($e[1], $e[2], $e[3], $e[4]);
417
		} elseif($code == 'CapHeight') {
418
			$fm['CapHeight'] = (int)$param;
419
		} elseif($code == 'StdVW') {
420
			$fm['StdVW'] = (int)$param;
421
		}
422
	}
423
	if (!isset($fm['FontName'])) {
424
		die('FontName not found');
425
	}
426
	if (!empty($map)) {
427
		if (!isset($widths['.notdef'])) {
428
			$widths['.notdef'] = 600;
429
		}
430
		if (!isset($widths['Delta']) AND isset($widths['increment'])) {
431
			$widths['Delta'] = $widths['increment'];
432
		}
433
		//Order widths according to map
434
		for ($i = 0; $i <= 255; $i++) {
435
			if (!isset($widths[$map[$i]])) {
436
				print "Warning: character ".$map[$i]." is missing\n";
437
				$widths[$i] = $widths['.notdef'];
438
			} else {
439
				$widths[$i] = $widths[$map[$i]];
440
			}
441
		}
442
	}
443
	$fm['Widths'] = $widths;
444
	return $fm;
445
}
446
 
447
function MakeFontDescriptor($fm, $symbolic=false) {
448
	//Ascent
449
	$asc = (isset($fm['Ascender']) ? $fm['Ascender'] : 1000);
450
	$fd = "array('Ascent'=>".$asc;
451
	//Descent
452
	$desc = (isset($fm['Descender']) ? $fm['Descender'] : -200);
453
	$fd .= ",'Descent'=>".$desc;
454
	//CapHeight
455
	if (isset($fm['CapHeight'])) {
456
		$ch = $fm['CapHeight'];
457
	} elseif (isset($fm['CapXHeight'])) {
458
		$ch = $fm['CapXHeight'];
459
	} else {
460
		$ch = $asc;
461
	}
462
	$fd .= ",'CapHeight'=>".$ch;
463
	//Flags
464
	$flags = 0;
465
	if (isset($fm['IsFixedPitch']) AND $fm['IsFixedPitch']) {
466
		$flags += 1<<0;
467
	}
468
	if ($symbolic) {
469
		$flags += 1<<2;
470
	} else {
471
		$flags += 1<<5;
472
	}
473
	if (isset($fm['ItalicAngle']) AND ($fm['ItalicAngle'] != 0)) {
474
		$flags += 1<<6;
475
	}
476
	$fd .= ",'Flags'=>".$flags;
477
	//FontBBox
478
	if (isset($fm['FontBBox'])) {
479
		$fbb = $fm['FontBBox'];
480
	} else {
481
		$fbb = array(0, ($desc - 100), 1000, ($asc + 100));
482
	}
483
	$fd .= ",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'";
484
	//ItalicAngle
485
	$ia = (isset($fm['ItalicAngle']) ? $fm['ItalicAngle'] : 0);
486
	$fd .= ",'ItalicAngle'=>".$ia;
487
	//StemV
488
	if (isset($fm['StdVW'])) {
489
		$stemv = $fm['StdVW'];
490
	} elseif (isset($fm['Weight']) AND preg_match('/(bold|black)/i', $fm['Weight'])) {
491
		$stemv = 120;
492
	} else {
493
		$stemv = 70;
494
	}
495
	$fd .= ",'StemV'=>".$stemv;
496
	//MissingWidth
497
	if(isset($fm['MissingWidth'])) {
498
		$fd .= ",'MissingWidth'=>".$fm['MissingWidth'];
499
	}
500
	$fd .= ')';
501
	return $fd;
502
}
503
 
504
function MakeWidthArray($fm) {
505
	//Make character width array
506
	$s = 'array(';
507
	$cw = $fm['Widths'];
508
	$els = array();
509
	$c = 0;
510
	foreach ($cw as $i => $w) {
511
		if (is_numeric($i)) {
512
			$els[] = (((($c++)%10) == 0) ? "\n" : '').$i.'=>'.$w;
513
		}
514
	}
515
	$s .= implode(',', $els);
516
	$s .= ')';
517
	return $s;
518
}
519
 
520
function MakeFontEncoding($map) {
521
	//Build differences from reference encoding
522
	$ref = ReadMap('cp1252');
523
	$s = '';
524
	$last = 0;
525
	for ($i = 32; $i <= 255; $i++) {
526
		if ($map[$i] != $ref[$i]) {
527
			if ($i != $last+1) {
528
				$s .= $i.' ';
529
			}
530
			$last = $i;
531
			$s .= '/'.$map[$i].' ';
532
		}
533
	}
534
	return rtrim($s);
535
}
536
 
537
function SaveToFile($file, $s, $mode='t') {
538
	$f = fopen($file, 'w'.$mode);
539
	if(!$f) {
540
		die('Can\'t write to file '.$file);
541
	}
542
	fwrite($f, $s, strlen($s));
543
	fclose($f);
544
}
545
 
546
function ReadShort($f) {
547
	$a = unpack('n1n', fread($f, 2));
548
	return $a['n'];
549
}
550
 
551
function ReadLong($f) {
552
	$a = unpack('N1N', fread($f, 4));
553
	return $a['N'];
554
}
555
 
556
function CheckTTF($file) {
557
	//Check if font license allows embedding
558
	$f = fopen($file, 'rb');
559
	if (!$f) {
560
		die('Error: unable to open '.$file);
561
	}
562
	//Extract number of tables
563
	fseek($f, 4, SEEK_CUR);
564
	$nb = ReadShort($f);
565
	fseek($f, 6, SEEK_CUR);
566
	//Seek OS/2 table
567
	$found = false;
568
	for ($i = 0; $i < $nb; $i++) {
569
		if (fread($f, 4) == 'OS/2') {
570
			$found = true;
571
			break;
572
		}
573
		fseek($f, 12, SEEK_CUR);
574
	}
575
	if (!$found) {
576
		fclose($f);
577
		return;
578
	}
579
	fseek($f, 4, SEEK_CUR);
580
	$offset = ReadLong($f);
581
	fseek($f, $offset, SEEK_SET);
582
	//Extract fsType flags
583
	fseek($f, 8, SEEK_CUR);
584
	$fsType = ReadShort($f);
585
	$rl = ($fsType & 0x02) != 0;
586
	$pp = ($fsType & 0x04) != 0;
587
	$e = ($fsType & 0x08) != 0;
588
	fclose($f);
589
	if($rl AND (!$pp) AND (!$e)) {
590
		print 'Warning: font license does not allow embedding.'."\n";
591
	}
592
}
593
 
594
// -------------------------------------------------------------------
595
 
596
$arg = $GLOBALS['argv'];
597
if (count($arg) >= 3) {
598
	ob_start();
599
	array_shift($arg);
600
	if (sizeof($arg) == 3) {
601
		$arg[3] = $arg[2];
602
		$arg[2] = true;
603
	} else {
604
		if (!isset($arg[2])) {
605
			$arg[2] = true;
606
		}
607
		if (!isset($arg[3])) {
608
			$arg[3] = 'cp1252';
609
		}
610
	}
611
	if (!isset($arg[4])) {
612
		$arg[4] = array();
613
	}
614
	MakeFont($arg[0], $arg[1], $arg[2], $arg[3], $arg[4]);
615
	$t = ob_get_clean();
616
	print preg_replace('!<BR( /)?>!i', "\n", $t);
617
} else {
618
	print 'Usage: makefont.php <ttf/otf/pfb file> <afm/ufm file> <encoding> <patch>'."\n";
619
}
620
 
621
//============================================================+
622
// END OF FILE
623
//============================================================+