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