replace $char to \rule if $char does not have IDS.
[chise/omega.git] / outCMAP
1 #!/usr/bin/perl
2
3 # ver.0.2
4
5 use strict;
6 use vars qw($omegadb_path
7             $opt_protrude $opt_order
8             $opt_in_cs $opt_out_cs
9             $opt_help $usage
10             $in_cs $out_cs $i @chars
11             @order %order
12             $char $char_id $out_char
13             $ids $ids_argc %ids $idsdb
14             $idsdata_file $ids_start $font_start
15             %utf8mcs_map_from
16             $inotp $perl56 $perl58
17             @CDP @HZK @GT
18             );
19 use Getopt::Long;
20 use utf8;
21 use Fcntl ':flock';
22 use Chise_utils ':all';
23
24 my $omegadb_path="/usr/local/lib/chise/omega";
25
26 ### Options ###
27
28 $opt_order='jtcgkh';
29 $opt_protrude=0;# 1=true, 0=false.
30
31 ###
32 my $strictly_forbidden_after = '「【『[(〈“‘‘(〔{《{\[\(\x{3016}{「';
33 #       \x{3016} | # white 【
34
35 my $forbidden_after = "\x{0000}";
36
37 # ¥¥$$〒♯##¢¢££@@§
38 my $slightly_forbidden_after = '¥¥$$〒♯##¢¢££@@§';
39
40 # $strictly_forbidden_before
41 # All these characters are allowed to protrude
42 # in the right margin
43 my $strictly_forbidden_before=
44     '!,.:;?、。!,.:;?。\)#}’”〉》」』】〕\x{3017})]}}」\]';
45 ###       \x{3017} | # white 】
46
47 my $forbidden_before
48     = 'ー々ぁぃぅぇぉゃゅょっゎァィゥェォャュョッヮヵヶ';
49
50 my $slightly_forbidden_before
51     = '\x{000a}\#\-‐−‰′″℃゛゜ゝゞヽヾ"%-゙゚';
52
53 my $asian = '\x{1100}-\x{11FF}\x{2E80}-\x{D7AF}\x{F900}-\x{FAFF}\x{FE30}-\x{FE4F}\x{FF00}-';
54
55 my $space = '\x{0020}\x{0009}\x{000A}\x{000C}\x{000D}';
56
57 if($^V and $^V ge v5.8){
58     $perl58=1;
59 }elsif($^V and $^V ge v5.6){
60     $perl56=1;
61 }else{
62     print STDERR "This versin is not supported.";
63     exit 1;
64 }
65 if($perl58){
66     eval "use Encode";
67     binmode(STDIN, ':encoding(utf8)');
68     binmode(STDOUT, ':encoding(utf8)');
69 }
70
71 &GetOptions("in=s"=>\$opt_in_cs,
72             "out=s"=>\$opt_out_cs,
73             "help",\$opt_help);
74
75 $usage=<<EOF;
76 Usage: $0 -i <input coding system> -o <cmap encoding>
77     input coding system:
78       Utf8mcs, Utf8cns, Utf8gb, Utf8jis, Utf8ks
79     cmap encoding:
80       UniCNS, UniGB, UniJIS, UniKS, UniMulti
81 EOF
82
83 if($opt_in_cs or $opt_out_cs){
84     $in_cs=$opt_in_cs;
85     $out_cs=$opt_out_cs;
86 }elsif(@ARGV==0){
87     ($in_cs,$out_cs)=($0=~/(Utf8.+)To(\w+)/);
88     $inotp=1;
89 }
90
91 # $in_cs:
92 #   Utf8mcs,Utf8cns,Utf8gb,Utf8jis,Utf8ks,
93 # $out_cs:
94 #   UniCNS,UniGB,UniJIS,UniKS,UniMulti
95
96 $in_cs=~s/Utf8/ucs\@/;
97
98 if($opt_help
99    or not defined($in_cs)
100    or not defined($out_cs)){
101     print $usage;
102     exit 1;
103 }
104
105 $omegadb_path=~s!/$!!;
106
107 $idsdata_file="$omegadb_path/idsdata.pl";
108 $ids_start=0x00; 
109 $font_start=0;
110
111 if(-e $idsdata_file){
112     open(IDSDATA,"+<$idsdata_file") or die;
113     flock(IDSDATA,LOCK_EX);
114     seek(IDSDATA,0,0);
115     while(<IDSDATA>){
116         eval $_;
117     }
118     seek(IDSDATA,0,0);
119 #         require $idsdata_file;
120 }else{
121     open(IDSDATA,">$idsdata_file") or die;
122     flock(IDSDATA,LOCK_EX);
123     seek(IDSDATA,0,0);
124 }
125
126 $ids_argc=0;
127 $ids="";
128
129 @GT=(#"=gt","=gt-k",
130      "=gt-pj-1","=gt-pj-2","=gt-pj-3","=gt-pj-4","=gt-pj-5","=gt-pj-6","=gt-pj-7","=gt-pj-8","=gt-pj-9","=gt-pj-10","=gt-pj-11"
131      #,"=gt-pj-k1","=gt-pj-k2"
132      );
133 @HZK=("=hanziku-1","=hanziku-2","=hanziku-3","=hanziku-4","=hanziku-5","=hanziku-6","=hanziku-7","=hanziku-8","=hanziku-9","=hanziku-10","=hanziku-11","=hanziku-12");
134 @CDP=("=big5-cdp");
135
136 %order=('c'=>'UniCNS',
137         'g'=>'UniGB',
138         'j'=>'UniJIS',
139         'k'=>'UniKS',
140         't'=>'GT',
141         'h'=>'HZK',
142         # not implemented yet.
143         # 'd'=>'CDP',
144         );
145
146 if(defined($opt_order)){
147     if($opt_order=~/^[cgjkth]*$/){
148         @order=split(//,$opt_order);
149         @order=map {$order{$_}} @order;
150     }else{
151         print STDERR "Invalid order!\n";
152         exit 1;
153     }
154 }
155
156 while(<>){
157     if($perl56){
158         # for perl 5.6
159         if($inotp){
160             s/(.)/pack("c",&get_char_id(unpack("U",$1),$in_cs))/ge;
161         }else{
162             s/(.)/pack("U",&get_char_id(unpack("U",$1),$in_cs))/ge;
163         }
164     }elsif($perl58){
165         # for perl 5.8.
166         $_=decode('utf8', $_);
167         s/(.)/pack("U",&get_char_id(unpack("U",$1),$in_cs))/ge;
168     }
169     s/(amp.+?;)/&de_er($1)/ge;
170 #    s/(&.+?;)/&de_er($1)/ge;
171     @chars=split(//);
172     for($i=0;$i<=$#chars;$i++){
173         $char=$chars[$i];
174         $char_id=unpack("U",$char);
175
176         if($char_id<=0x20){
177             print $chars[$i];
178             next;
179         }elsif($char_id>0x20 and $char_id<=0x02ff){
180             # Basic Latin
181             # Latin-1 Supplement
182             # Latin Extended-A
183             # Latin Extended-B
184             # IPA Extensions
185             print &latin_parse();
186             next;
187         }elsif($char_id>=0x2ff0 and $char_id<=0x2fff){
188             # Ideographic Description Characters
189             print &ids_parse();
190             next;
191         }else{
192             if(($out_char=&get_output_char($char_id,$out_cs))){
193                 print $out_char,&add_break($i);
194             }elsif($char_id >= 0x20000 && $char_id <=0x2a6df){
195                 # CJK Unified Ideographs Extension B
196                 if(not defined($ids{$char}) and $ids{$char}[1]>=0){
197                     $ids{$char}[0]=$font_start;
198                     $ids{$char}[1]=$ids_start;
199                     $ids_start++;
200                     if($ids_start>255){
201                         $ids_start=0;
202                         $font_start++;
203                     }
204                 }
205                 print "{\\fontencoding{OT1}\\fontfamily{" .
206                     sprintf("chise%03d",$ids{$char}[0]) .
207                     "}\\selectfont\\char$ids{$char}[1]}",&add_break($i);
208                 next;
209             }else{
210                 if($ids=&get_ids($char)){
211                     print &get_macro_for_ids($ids),&add_break($i);
212                 }else{
213                     print '\rule{1ex}{1ex}',&add_break($i);
214                 }
215             }
216         }
217     }
218 }
219
220 print IDSDATA 'use utf8;',"\n";
221 foreach $ids (keys %ids){
222     print IDSDATA '$ids{\'',$ids,'\'}=[',join ",",@{$ids{$ids}},"];\n" if($perl56);
223     print IDSDATA '$ids{\'',encode('utf8',$ids),'\'}=[',join ",",@{$ids{$ids}},"];\n" if($perl58);
224 }
225 print IDSDATA '$font_start=',$font_start,";\n";
226 print IDSDATA '$ids_start=',$ids_start,";\n";
227 print IDSDATA "1;";
228 flock(IDSDATA,LOCK_UN);
229
230 exit 0;
231
232 sub add_break{
233     my($i)=@_;
234
235     if($i<($#chars-1)){
236         if(($chars[$i+1]=~m/[$strictly_forbidden_before]/o)
237            and($chars[$i+2]=~m/[$strictly_forbidden_before]/o)){
238             return "\\CJKunbreakablekernone ";
239         }elsif($opt_protrude){
240             if(($chars[$i+1]=~m/[$strictly_forbidden_before]/o)
241                and($chars[$i+2]=~m/[^$strictly_forbidden_before]/o)){
242                 return "\\CJKunbreakablekernone \\CJKprotrude ";
243             }
244         }
245     }
246     if(($i<$#chars)
247        and($chars[$i+1]=~m/[$strictly_forbidden_before]/o)){
248         return "\\CJKunbreakablekernone ";
249     }
250     if($chars[$i]=~m/[$strictly_forbidden_after]/o){
251         return "\\CJKunbreakablekernone ";
252     }
253     if(($i<$#chars)
254        and($chars[$i+1]=~m/[$forbidden_before]/o)){
255         return "\\CJKunbreakablekerntwo ";
256
257     }
258     if($chars[$i]=~m/[$forbidden_after]/o){
259         return "\\CJKunbreakablekerntwo ";
260     }
261     if(($i<$#chars)
262        and($chars[$i+1]=~m/[$slightly_forbidden_before]/o)){
263         return "\\CJKunbreakablekernthree ";
264     }
265     if($chars[$i]=~m/[$slightly_forbidden_after]/o){
266         return "\\CJKunbreakablekernthree ";
267     }
268     if($chars[$i]=~m/[$asian]/o){
269         return "\\CJKbreakablekern ";
270     }
271     if(($i<$#chars)and($chars[$i+1]=~m/[$asian]/o)){
272         return "\\CJKbreakablekern ";
273     }
274 }
275
276 sub latin_parse{
277     # arguments: none
278     # return: string for output with TeX macro.
279     my($char_id);
280     my $out_str=$chars[$i];
281     $i++;
282     while($i<=$#chars){
283         $char_id=unpack("U",$chars[$i]);
284         if($char_id<=0xff){
285             $out_str.=pack("U",$char_id);
286         }else{
287             $i--;
288             last;
289         }
290         $i++;
291     }
292     return '{\normalfont {'.$out_str.'}}';
293 }
294
295 sub ids_parse{
296     # arguments: none
297     # return: character for output,
298     #          TeX macro for ids,
299     #          or GETA character if ids is invalid.
300     my($ids,$ids_argc)=&ids_rest("",0,$chars[$i]);
301
302     while($ids_argc>0){
303         # We are in IDS.
304         $i++;
305         if($i>$#chars){
306             print STDERR "IDS parse error: $ids\n";
307 #           return pack("U",0xfffd);
308             return pack("U",0x3013);
309         }
310
311         ($ids,$ids_argc)=&ids_rest($ids,$ids_argc,$chars[$i]);
312         if($ids_argc==0){
313             if(($char_id=&get_char_id_for_ids($ids))
314                and($out_char=&get_output_char($char_id,$out_cs))){
315                 return $out_char;
316             }else{
317                 return &get_macro_for_ids($ids) if($perl56);
318                 return encode('utf8', &get_macro_for_ids($ids)) if($perl58);
319             }
320         }
321     }
322 }
323
324 sub ids_rest{
325     # arguments: <ids>, <rest number of arguments for ids>, <character>
326     # return: ids and rest number of arguments for ids.
327     my($ids,$ids_argc,$char)=@_;
328     my($argc);
329     $argc=&ids_argc($char);
330     if($argc){
331         $ids_argc+= $ids_argc==0 ? $argc : $argc-1;
332     }else{
333         $ids_argc--;
334     }
335     $ids.=$char if($perl56);
336     $ids.=encode('utf8',$char) if($perl58);
337     return ($ids,$ids_argc);
338 }
339
340 sub get_macro_for_ids{
341     # argument: <ids>
342     # return: TeX macro for ids
343     #          or GETA character if ids is invalid for KAGE.
344     my($ids)=@_;
345     $ids=&normalize_ids($ids,"UniJIS");
346 #    return pack("U",0xfffd) if(($ids!~/[$idc]/)
347     return pack("U",0x3013) if(($ids!~/[$idc]/)
348                                or($ids=~/[\x{10000}-]/));
349                     #irregular for KAGE.
350     if(not defined($ids{$ids}) and $ids{$ids}[1]>=0){
351         $ids{$ids}[0]=$font_start;
352         $ids{$ids}[1]=$ids_start;
353         $ids_start++;
354     }
355     if($ids_start>255){
356         $ids_start=0;
357         $font_start++;
358     }
359     return "{\\fontencoding{OT1}\\fontfamily{"
360         .sprintf("chise%03d",$ids{$ids}[0])
361         ."}\\selectfont\\char$ids{$ids}[1]}";
362 }
363
364 sub normalize_ids{
365     # argument: <ids>, <output coding system>
366     # return: ids or GETA character if ids is invalid for KAGE.
367     my($ids,$out_cs)=@_;
368     $ids = decode('utf8', $ids) if $perl58;
369     $out_cs=~s/Uni(.+)/'ucs@'.lc($1)/e;
370
371     my $output_ids="";
372     my($char,$char_id,$output_char_id);
373     while($ids=~m/(.)/g){
374         $char=$1;
375         $char_id=unpack("U",$char);
376         if($char=~/[$idc]/){
377             $output_ids.=$char;
378         }elsif($output_char_id=&get_char_attribute($char,"=$out_cs")){
379             $output_ids.=pack("U",$output_char_id);
380         }elsif($output_char_id=&get_char_attribute($char,"=ucs")){
381             $output_ids.=pack("U",$output_char_id);
382         }else{
383 #           return pack("U",0xfffd);
384             return pack("U",0x3013);
385         }
386     }
387     return $output_ids;
388 }
389
390 sub get_output_char{
391     # argument: <char-id>
392     # return: character in output coding system or TeX macro.
393     my($char_id,$out_cs)=@_;
394     my($char,$out_char_id,$suffix);
395     my($gt,$hzk,$cdp);
396
397     $char=pack('U',$char_id);
398
399     if($out_cs eq 'UniJIS'
400        and &get_char_attribute($char,"adobe-unijis-utf16-h")){
401         if($out_char_id=&get_char_attribute($char,'=ucs@jis')
402            or $out_char_id=&get_char_attribute($char,'=ucs')
403            or $out_char_id=&get_char_attribute($char,'=>ucs@jis')
404            or $out_char_id=&get_char_attribute($char,'=>ucs')
405            ){
406             return pack("U",$out_char_id);
407         }
408     }elsif($out_cs eq 'UniGB'
409            and &get_char_attribute($char,"adobe-unigb-ucs2-h")){
410         if($out_char_id=&get_char_attribute($char,'=ucs@gb')
411            or $out_char_id=&get_char_attribute($char,'=ucs')
412            or $out_char_id=&get_char_attribute($char,'=>ucs@gb')
413            or $out_char_id=&get_char_attribute($char,'=>ucs')
414            ){
415             return pack("U",$out_char_id);
416         }
417     }elsif($out_cs eq 'UniCNS'
418            and &get_char_attribute($char,"adobe-unicns-ucs2-h")){
419         if($out_char_id=&get_char_attribute($char,'=ucs@cns')
420            or $out_char_id=&get_char_attribute($char,'=ucs')
421            or $out_char_id=&get_char_attribute($char,'=>ucs@cns')
422            or $out_char_id=&get_char_attribute($char,'=>ucs')
423            ){
424             return pack("U",$out_char_id);
425         }
426     }elsif($out_cs eq 'UniKS'
427            and &get_char_attribute($char,"adobe-uniks-ucs2-h")){
428         if($out_char_id=&get_char_attribute($char,'=ucs@ks')
429            or $out_char_id=&get_char_attribute($char,'=ucs')
430            or $out_char_id=&get_char_attribute($char,'=>ucs@ks')
431            or $out_char_id=&get_char_attribute($char,'=>ucs')
432            ){
433             return pack("U",$out_char_id);
434         }
435     }elsif($out_cs eq 'UniMulti'){
436         foreach $out_cs (@order){
437     
438             if($out_cs eq 'UniJIS'
439                and &get_char_attribute($char,"adobe-unijis-utf16-h")){
440                 if($out_char_id=&get_char_attribute($char,'=ucs@jis')
441                    or $out_char_id=&get_char_attribute($char,'=ucs')
442                    or $out_char_id=&get_char_attribute($char,'=>ucs@jis')
443                    or $out_char_id=&get_char_attribute($char,'=>ucs')
444                    ){
445                     return '{\selectjisfont\char'.$out_char_id.'}';
446                 }
447             }elsif($out_cs eq 'UniGB'
448                    and &get_char_attribute($char,"adobe-unigb-ucs2-h")){
449                 if($out_char_id=&get_char_attribute($char,'=ucs@gb')
450                    or $out_char_id=&get_char_attribute($char,'=ucs')
451                    or $out_char_id=&get_char_attribute($char,'=>ucs@gb')
452                    or $out_char_id=&get_char_attribute($char,'=>ucs')
453                    ){
454                     return '{\selectgbsfont\char'.$out_char_id.'}';
455                 }
456             }elsif($out_cs eq 'UniCNS'
457                    and &get_char_attribute($char,"adobe-unicns-ucs2-h")){
458                 if($out_char_id=&get_char_attribute($char,'=ucs@cns')
459                    or $out_char_id=&get_char_attribute($char,'=ucs')
460                    or $out_char_id=&get_char_attribute($char,'=>ucs@cns')
461                    or $out_char_id=&get_char_attribute($char,'=>ucs')
462                    ){
463                     return '{\selectcnsfont\char'.$out_char_id.'}';
464                 }
465             }elsif($out_cs eq 'UniKS'
466                    and &get_char_attribute($char,"adobe-uniks-ucs2-h")){
467                 if($out_char_id=&get_char_attribute($char,'=ucs@ks')
468                    or $out_char_id=&get_char_attribute($char,'=ucs')
469                    or $out_char_id=&get_char_attribute($char,'=>ucs@ks')
470                    or $out_char_id=&get_char_attribute($char,'=>ucs')
471                    ){
472                     return '{\selectksxfont\char'.$out_char_id.'}';
473                 }
474             }elsif($out_cs eq 'GT'){
475                 return $gt if($gt=&get_macro_for_GT($char_id));
476             }elsif($out_cs eq 'HZK'){
477                 return $hzk if($hzk=&get_macro_for_HZK($char_id));
478             }elsif($out_cs eq 'CDP'){
479                 return $cdp if($cdp=&get_macro_for_CDP($char_id));
480             }
481         }
482     }
483     return undef;
484 }
485
486 sub get_ids{
487     # argument: <character>
488     # return: ids
489     my($char)=@_;
490     my $ids="";
491     $ids=&get_char_attribute($char,"ids-aggregated")
492         or &get_char_attribute($char,"ids");
493 #       or &get_char_attribute($char,"ideographic-structure");
494     $ids=decode('utf8', $ids) if($perl58);
495 #    $ids=~s/[? ()]//g;
496     return $ids;
497 }
498
499 sub get_char_id_for_ids{
500     # argument: <ideographic description sequence>
501     # return: char-id
502     my($ids)=@_;
503     my($output_char);
504     $ids=decode('utf8', $ids) if($perl58);
505
506     if(($output_char)=&get_chars_matching("ids",$ids)){
507         return unpack("U",$output_char);
508     }else{
509         return undef;
510     }
511 }
512
513 sub get_char_id{
514     # argument: <char-id>, <input coding system>
515     # return:   char-id.
516     my($char_id,$in_cs)=@_;
517     my($output_char);
518
519     return $char_id if($in_cs eq 'ucs@mcs');
520
521     if(($output_char)=&get_chars_matching("=$in_cs",$char_id)){
522         return unpack("U",$output_char);
523     }else{
524         return $char_id;
525     }
526 }
527
528 sub get_macro_for_GT{
529     # argument: <char-id>
530     # return: TeX macro for GT fonts.
531     my($char_id)=@_;
532     my($char,$gt,$GT);
533     $char=pack("U",$char_id);
534     foreach (@GT){
535         if($gt=&get_char_attribute($char,$_)){
536             m/gt\-pj\-(\d+)/ and $GT=$1;
537             last;
538         }
539     }
540     if($gt){
541         return "{\\fontencoding{OT1}\\fontfamily{"
542             .sprintf("gt%02d",$GT)
543             ."}\\selectfont\\char".($gt|0x8080)."}";
544     }else{
545         return undef;
546     }
547 }
548
549 sub get_macro_for_HZK{
550     my($char_id)=@_;
551     my($char,$hzk,$HZK);
552     $char=pack("U",$char_id);
553     foreach (@HZK){
554         if($hzk=&get_char_attribute($char,$_)){
555             m/hanziku\-(\d+)/ and $HZK=$1;
556             last;
557         }
558     }
559     if($hzk){
560         return "{\\fontencoding{OT1}\\fontfamily{".sprintf("hzk%02d",$HZK)."}\\selectfont\\char".$hzk."}";
561     }else{
562         return undef;
563     }
564 }
565
566 sub get_macro_for_CDP{
567     my($char_id)=@_;
568     my($char,$cdp);
569     $char=pack("U",$char_id);
570     foreach (@CDP){
571         if($cdp=&get_char_attribute($char,$_)){
572             last;
573         }
574     }
575     if($cdp){
576         return "{\\fontencoding{OT1}\\fontfamily{cdp}\\selectfont\\char".$cdp."}";
577     }else{
578         return undef;
579     }
580 }