PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/framework/lib/classes/PHPExcel/Shared/PDF/fonts/utils/makefont.php

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