greenstone.org greenstone wiki greenstone trac planet greenstone

root/other-projects/trunk/image-generation/shape_arabic.pl

http://svn.greenstone.org/other-projects/trunk/image-generation/shape_arabic.pl
Revision 13536, 9.5 kB (checked in by mdewsnip, 2 years ago)

Initial revision

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1 #!/usr/bin/perl
2 # -*-Perl-*-
3
4 #---
5 # $Id$
6 #
7 # ------------
8 # Description:
9 # ------------
10 #  Perl script that does arabic utf-8 shaping
11 #
12 #  - Note how easy shaping really is.
13 #  - This should be used an a learning tool for those interested
14 #    in Arabic and in perl (its a learning medium).
15 #
16 # -----------------
17 # Revision Details:    (Updated by Revision Control System)
18 # -----------------
19 #  $Date$
20 #  $Author$
21 #  $Revision$
22 #  $Source$
23 #
24 # (www.arabeyes.org - under GPL license)
25 #---
26
27 # Point to and use Perl version 5.8 (or greater)
28 use 5.8.0;
29
30 # Try to be clean in variable usage, etc
31 use strict;
32
33 # Enable full UTF-8 support
34 use utf8;
35 use open ':utf8';
36 binmode(STDOUT, ":utf8");
37
38 # Enable Automatic command-line parsing
39 require "newgetopt.pl";
40
41 my (
42     $this_script,
43     $opt_debug,
44     $opt_file,
45     $out_line,
46    );
47
48 # Specify global variable values and init some Variables
49 $this_script    = $0;
50 $this_script    =~ s|^.*/([^/]*)|$1|;
51
52 # Process the command line
53 &get_args();
54
55 if (! $opt_file )
56 {
57     print "ERROR($this_script): Specify input file to process with '-file' option\n";
58     &usage(1);
59 }
60
61 open (INFILE, "< $opt_file") or die "Can't open $opt_file - $! \n";
62
63 while (<INFILE>)
64 {
65     $out_line .= &shape_line($_);
66 }
67 print $out_line;
68
69 exit(0);
70
71        ###        ###
72 ######## Procedures ########
73        ###        ###
74
75 ##
76 # Do the actual Arabic Shaping
77 sub shape_line
78 {
79     my ($line)          = @_;
80
81     use constant ISOLATED       => 0;
82     use constant INITIAL        => 1;
83     use constant MEDIAL         => 2;
84     use constant FINAL          => 3;
85
86     my (
87         @char,
88         $cur,
89         $prev,
90         $next,
91         $index,
92         $prev_index,
93         $next_index,
94         $out,
95         $str,
96        );
97
98     # Hex values noted for all entries.
99     #          iso-8859-6       =>      [     s,      i,      m,      f]
100     my %map     = (
101                    "621"        =>      ["FE80",      0,      0,      0],       # HAMZA
102                    "622"        =>      ["FE81",      0,      0, "FE82"],       # ALEF_MADDA
103                    "623"        =>      ["FE83",      0,      0, "FE84"],       # ALEF_HAMZA_ABOVE
104                    "624"        =>      ["FE85",      0,      0, "FE86"],       # WAW_HAMZA
105                    "625"        =>      ["FE87",      0,      0, "FE88"],       # ALEF_HAMZA_BELOW
106                    "626"        =>      ["FE89", "FE8B", "FE8C", "FE8A"],       # YEH_HAMZA
107                    "627"        =>      ["FE8D",      0,      0, "FE8E"],       # ALEF
108                    "628"        =>      ["FE8F", "FE91", "FE92", "FE90"],       # BEH
109                    "629"        =>      ["FE93",      0,      0, "FE94"],       # TEH_MARBUTA
110                    "62A"        =>      ["FE95", "FE97", "FE98", "FE96"],       # TEH
111                    "62B"        =>      ["FE99", "FE9B", "FE9C", "FE9A"],       # THEH
112                    "62C"        =>      ["FE9D", "FE9F", "FEA0", "FE9E"],       # JEEM
113                    "62D"        =>      ["FEA1", "FEA3", "FEA4", "FEA2"],       # HAH
114                    "62E"        =>      ["FEA5", "FEA7", "FEA8", "FEA6"],       # KHAH
115                    "62F"        =>      ["FEA9",      0,      0, "FEAA"],       # DAL
116                    "630"        =>      ["FEAB",      0,      0, "FEAC"],       # THAL
117                    "631"        =>      ["FEAD",      0,      0, "FEAE"],       # REH
118                    "632"        =>      ["FEAF",      0,      0, "FEB0"],       # ZAIN
119                    "633"        =>      ["FEB1", "FEB3", "FEB4", "FEB2"],       # SEEN
120                    "634"        =>      ["FEB5", "FEB7", "FEB8", "FEB6"],       # SHEEN
121                    "635"        =>      ["FEB9", "FEBB", "FEBC", "FEBA"],       # SAD
122                    "636"        =>      ["FEBD", "FEBF", "FEC0", "FEBE"],       # DAD
123                    "637"        =>      ["FEC1", "FEC3", "FEC4", "FEC2"],       # TAH
124                    "638"        =>      ["FEC5", "FEC7", "FEC8", "FEC6"],       # ZAH
125                    "639"        =>      ["FEC9", "FECB", "FECC", "FECA"],       # AIN
126                    "63A"        =>      ["FECD", "FECF", "FED0", "FECE"],       # GHAIN
127                    "640"        =>      [ "640",      0,      0,      0],       # TATWEEL
128                    "641"        =>      ["FED1", "FED3", "FED4", "FED2"],       # FEH
129                    "642"        =>      ["FED5", "FED7", "FED8", "FED6"],       # QAF
130                    "643"        =>      ["FED9", "FEDB", "FEDC", "FEDA"],       # KAF
131                    "644"        =>      ["FEDD", "FEDF", "FEE0", "FEDE"],       # LAM
132                    "645"        =>      ["FEE1", "FEE3", "FEE4", "FEE2"],       # MEEM
133                    "646"        =>      ["FEE5", "FEE7", "FEE8", "FEE6"],       # NOON
134                    "647"        =>      ["FEE9", "FEEB", "FEEC", "FEEA"],       # HEH
135                    "648"        =>      ["FEED",      0,      0, "FEEE"],       # WAW
136                    "649"        =>      ["FEEF",      0,      0, "FEF0"],       # ALEF_MAKSURA
137                    "64A"        =>      ["FEF1", "FEF3", "FEF4", "FEF2"],       # YEH
138                    # exceptions
139                    "644622"     =>      ["FEF5",      0,      0, "FEF6"],       # LAM_ALEF_MADDA
140                    "644623"     =>      ["FEF7",      0,      0, "FEF8"],       # LAM_ALEF_HAMZA_ABOVE
141                    "644625"     =>      ["FEF9",      0,      0, "FEFA"],       # LAM_ALEF_HAMZA_BELOW
142                    "644627"     =>      ["FEFB",      0,      0, "FEFC"],       # LAM_ALEF
143
144                    ## ---- My additions for Farsi ----
145                    # I think there is a bug in this script which causes an initial "Peh"
146                    # to be treated as isolated. This is why the isolated value for "Peh"
147                    # is set to the initial value.
148                    "67E"        =>     [ "FB58", "FB58", "FB59", "FB57" ],      # FARSI PEH
149                    "686"        =>     [ "FB7A", "FB7C", "FB7D", "FB7B" ],      # FARSI TCHEH
150                    "6AF"        =>     [ "FB92", "FB94", "FB95", "FB93" ],      # FARSI GAF
151                    "6CC"        =>     [ "FBFC", "FBFE", "FBFF", "FBFD" ],      # FARSI YEH
152                   );
153
154     my %special_is_next         = ( "640" => 1 );
155     my %special_is_comb1        = ( "644" => 1 );
156     my %special_is_comb2        = (
157                                     "622" => 1,
158                                     "623" => 1,
159                                     "625" => 1,
160                                     "627" => 1,
161                                   );
162
163     my %special_is_composing    = (
164                                     "64B" => 1,         # FATHATAN
165                                     "64C" => 1,         # DAMMATAN
166                                     "64D" => 1,         # KASRATAN
167                                     "64E" => 1,         # FATHA
168                                     "64F" => 1,         # DAMMA
169                                     "650" => 1,         # KASRA
170                                     "651" => 1,         # SHADDA
171                                     "652" => 1,         # SUKUN
172                                     "653" => 1,         # MADDAH ABOVE
173                                     "654" => 1,         # HAMZA ABOVE
174                                     "655" => 1,         # HAMZA BELOW
175                                     "670" => 1,         # SUPERSCRIPT ALEF
176                                     "6D6" => 1,         # HIGH LIG. SAD WITH LAM WITH ALEF MAKSURA
177                                     "6D7" => 1,         # HIGH LIG. QAF WITH LAM WITH ALEF MAKSURA
178                                     "6D8" => 1,         # HIGH MEEM INITIAL FORM
179                                     "6D9" => 1,         # HIGH LAM ALEF
180                                     "6DA" => 1,         # HIGH JEEM
181                                     "6DB" => 1,         # HIGH THREE DOTS
182                                     "6DC" => 1,         # HIGH SEEN
183 # The 2 entires below should not be here - contact unicode.org !!
184 #                                   "6DD" => 1,         # END OF AYAH
185 #                                   "6DE" => 1,         # START OF RUB EL HIZB
186                                     "6DF" => 1,         # HIGH ROUNDED ZERO
187                                     "6E0" => 1,         # HIGH UPRIGHT RECTANGULAR ZERO
188                                     "6E1" => 1,         # HIGH DOTLESS HEAD OF KHAH
189                                     "6E2" => 1,         # HIGH MEEM ISOLATED FORM
190                                     "6E3" => 1,         # LOW SEEN
191                                     "6E4" => 1,         # HIGH MADDA
192                                     "6E7" => 1,         # HIGH YEH
193                                     "6E8" => 1,         # HIGH NOON
194                                     "6EA" => 1,         # EMPTY CENTRE LOW STOP
195                                     "6EB" => 1,         # EMPTY CENTRE HIGH STOP
196                                     "6EC" => 1,         # HIGH STOP WITH FILLED CENTRE
197                                     "6ED" => 1,         # LOW MEEM
198                                   );
199
200     @char = split ("", $line);
201
202     for ($index = 0; $index <= $#char; $index++)
203     {
204         $cur = unpack("U", $char[$index]);
205         $cur = sprintf "\U%x\E", $cur;
206         if ( $opt_debug )
207         {
208             print "---- $index ----\n";
209             print "NOW - $cur \n";
210         }
211         if ( defined $map{$cur} )
212         {
213             # Previous character status
214             $prev_index = $index;
215
216           PREV_CHECK:
217             {
218                 $prev_index--;
219                 $prev = unpack("U", $char[$prev_index]);
220                 $prev = sprintf "\U%x\E", $prev;
221                 if ( $opt_debug )
222                 {
223                     print "prev - $prev \n";
224                 }
225
226                 # Get rid of all previous composers
227                 if ( $special_is_composing{$prev} )
228                 {
229                     redo PREV_CHECK;
230                 }
231             }
232            
233             if (
234                 ($index == 0)          ||
235                 !defined $map{$prev}   ||
236                 (!$map{$prev}[INITIAL] &&
237                  !$map{$prev}[MEDIAL]
238                 )
239                )
240             {
241                 # Don't have a 'prev'
242                 if ( $opt_debug )
243                 {
244                     print "prev not defined \n";
245                 }
246                 undef $prev;
247             }
248
249             # Next     character status
250             $next_index = $index;
251
252             NEXT_CHECK:
253             {
254                 $next_index++;
255                 $next = unpack("U", $char[$next_index]);
256                 $next = sprintf "\U%x\E", $next;
257                 if ( $opt_debug )
258                 {
259                     print "next - $next \n";
260                 }
261
262                 # Get rid of all next composers
263                 if ( $special_is_composing{$next} )
264                 {
265                     redo NEXT_CHECK;
266                 }
267             }
268
269             if (
270                 ($index == $#char)      ||
271                 !defined $map{$next}    ||
272                 (!$map{$next}[MEDIAL]   &&
273                  !$map{$next}[FINAL]    &&
274                  !$special_is_next{$next}
275                 )
276                )
277             {
278                 # Don't have a 'next'
279                 if ( $opt_debug )
280                 {
281                     print "next not defined \n";
282                 }
283                 undef $next;
284             }
285
286             # Shape the special combinational characters
287             if ( $special_is_comb1{$cur} )
288             {
289                 if (defined $next )
290                 {
291                     if ( $special_is_comb2{$next} )
292                     {
293                         $out = ( (defined $prev) ? $map{"$cur$next"}[FINAL] : $map{"$cur$next"}[ISOLATED] );
294                         $str .= pack("U", hex($out));
295                         if ( $opt_debug )
296                         {
297                             print "Got me a complex - use $out ($str)\n";
298                         }
299                         $index++;
300                         next;
301                     }
302                 }
303             }
304
305             # Medial
306             if ( (defined $prev) && (defined $next) )
307             {
308                 # In case there is no medial, move to next :-)
309                 if ( $out = $map{$cur}[MEDIAL] )
310                 {
311                     $str .= pack("U", hex($out));
312                     if ( $opt_debug )
313                     {
314                         print "Got prev & next - use $out ($str)\n";
315                     }
316                     next;
317                 }
318             }
319
320             # Final
321             if ( (defined $prev) )
322             {
323                 if ( $out = $map{$cur}[FINAL] )
324                 {
325                     $str .= pack("U", hex($out));
326                     if ( $opt_debug )
327                     {
328                         print "Got prev - use $out ($str)\n";
329                     }
330                     next;
331                 }
332             }
333
334             # Initial
335             if ( (defined $next) )
336             {
337                 if ( $out = $map{$cur}[INITIAL] )
338                 {
339                     $str .= pack("U", hex($out));
340                     if ( $opt_debug )
341                     {
342                         print "Got next - use $out ($str)\n";
343                     }
344                     next;
345                 }
346             }
347
348             # Isolated/Seperated (Stand-alone)
349             $out = $map{$cur}[ISOLATED];
350             $str .= pack("U", hex($out));
351             if ( $opt_debug )
352             {
353                 print "Got nothing - use $out ($str)\n";
354             }
355         }
356         else
357         {
358             $str .= pack("U", hex($cur));
359         }
360     }
361
362     if ( $opt_debug )
363     {
364         print "return - $str \n";
365     }
366     return ($str);
367 }
368
369 ##
370 # Print the simple usage
371 sub usage
372 {
373     my ($die_after)     = @_;
374
375     print "Usage: $this_script <-file filename>  [-debug] \n";
376     if ( $die_after ) { exit(5); }
377 }
378
379 ##
380 # Get the command line arguments
381 sub get_args
382 {
383   &NGetOpt (
384             "debug"     =>      \$opt_debug,                    # enable the printing of debug output
385             "file=s"    =>      \$opt_file,                     # specify a filename to process
386            ) || ( $? = 257, die "Invalid argument\n" );
387 }
Note: See TracBrowser for help on using the browser.