use $omegadb_path of Chise_utils.pm.
[chise/omega.git] / makefonts.pl
1 #!/usr/bin/perl -w -CSD
2 # This script requires 't1asm' program, which is part of t1utils package,
3 # and 'pfaedit'.
4
5 use strict;
6 use utf8;
7 use IO::Socket::INET;
8 use BerkeleyDB;
9 use Chise_utils '$omegadb_path';
10 require 5.008001;
11
12 #my $kageaddr="home.fonts.jp:5100"; # Specify port number!
13 my $kageaddr="home.fonts.jp:5101"; # Specify port number!
14 #my $kagecgi="/~kamichi/test/kagecgi.cgi"; #
15
16 my $t1asm = "/usr/bin/env t1asm"; # point to 't1asm' executable.
17 #die "Cannot execute $t1asm. Abort.\n" unless -x $t1asm;
18 my $pfaedit="/usr/bin/env pfaedit"; # point to 'pfaedit' executable.
19 #die "Cannot execute $pfaedit. Abort.\n" unless -x $pfaedit;
20
21 $omegadb_path=~s!/$!!;
22
23 my $cleanup_script=""; # Check the path of pfaedit script, if needed.
24
25 unless($cleanup_script){
26     if(-e "/usr/local/share/texmf/omega/ocp/local/chise/svg2t1.pfe"){
27         $cleanup_script="/usr/local/share/texmf/omega/ocp/local/chise/svg2t1.pfe";
28     }elsif(-e "/usr/share/texmf/omega/ocp/local/chise/svg2t1.pfe"){
29         $cleanup_script="/usr/share/texmf/omega/ocp/local/chise/svg2t1.pfe";
30     }elsif(-e "/sw/share/texmf/omega/ocp/local/chise/svg2t1.pfe"){
31         $cleanup_script="/sw/share/texmf/omega/ocp/local/chise/svg2t1.pfe";
32     }
33 }
34 unless($cleanup_script and -e $cleanup_script){
35     print STDERR "svg2t1.pfe not found!\n",
36     "please check \$cleanup_script in $0.\n";
37     exit 1;
38 }
39
40 my $dbpath="$omegadb_path/glyph.db"; # check your DB path.
41
42 # Open Glyph Database
43 my $db = new BerkeleyDB::Hash
44     -Filename => $dbpath, -Flags => DB_CREATE
45     || print STDERR "Cannot open $dbpath. Do not use glyph database.\n";
46
47 ####################
48 #### subroutines ###
49 ####################
50
51 sub printheader {
52 my ($fontname, $fullname, $familyname,
53     $weight, $version, $uniqueID,
54     $numchars,  $encoding) = @_;
55 return <<"HEADER";
56 %!PS-Adobe-Font-1.0: $fontname
57 11 dict begin
58 /PaintType 0 def
59 /FontType 1 def
60 /FontMatrix [0.001 0 0 0.001 0 0] readonly def
61 /UniqueID $uniqueID def
62 /FontBBox [0 -100 1000 900 ]readonly def
63 /FontInfo 8 dict dup begin
64  /version ($version) readonly def
65  /Notice (Copyright (C) Chise Project; Glyphs generated by KAGE server) readonly def
66  /FullName ($fullname) readonly def
67  /FamilyName ($familyname) readonly def
68  /Weight ($weight) readonly def
69  /ItalicAngle 0 def
70  /isFixedPitch true def
71  /UnderlinePosition -200 def
72 end readonly def
73 $encoding
74 currentfile eexec
75 dup
76 /Private 9 dict dup begin
77 /-|{string currentfile exch readstring pop}executeonly def
78 /|-{noaccess def}executeonly def
79 /|{noaccess put}executeonly def
80 /BlueValues [] noaccess ND
81 /UniqueID $uniqueID def
82 /MinFeature{16 16} |-
83 /ForceBold false def
84 /password 5839 def
85 /lenIV 4 def
86 end readonly def
87 2 index /CharStrings $numchars dict dup begin
88 HEADER
89 }
90
91 sub printfooter {
92 return <<"FOOTER";
93 /.notdef {
94         0 1000 hsbw
95         endchar
96         } |-
97 end
98 end
99 readonly put
100 noaccess put
101 dup/FontName get exch definefont pop
102 mark currentfile closefile
103 cleartomark
104 FOOTER
105 }
106
107 # Convert svg to Type1 charstring.
108 # Return: Type1 charstring.
109 sub svg2charstring {
110     my ($svg) = @_;
111     my @paths= split(/\n/, $svg);
112     my ($x1, $y1, $x2, $y2);
113     my $glyph= "\{\n0 1000 hsbw\n";
114     for (@paths){
115         if(m/error/i){
116             return undef;
117         }
118         next unless /\<path d=\"M([0-9, ]+)Z\"\/\>/;
119         my $path = $1; $path =~ s/^ +//; $path =~ s/ +$//;
120         my @point_pair = split(/ /, $path);
121         ($x1, $y1) =  split(/,/, shift(@point_pair));
122         $y1=1000-$y1-100;
123         if(defined $x2){
124             $glyph.=sprintf("%d %d rmoveto\n", $x1-$x2, $y1-$y2);
125         }else{
126             $glyph.= "$x1 $y1 rmoveto\n";
127         }
128         foreach my $pair (@point_pair) {
129             ($x2, $y2) = split(/,/, $pair);
130             $y2=1000-$y2-100;
131             $glyph.=sprintf("%d %d rlineto\n", $x2-$x1, $y2-$y1);
132             $x1=$x2; $y1=$y2;
133         }
134         $glyph.= "closepath\n";
135     }
136     return $glyph.= "endchar\n\} |-\n";
137 }
138
139 # Query KAGE server and generate Type1 charstrings.
140 # Return: charstrings, encoding vector, and number of chars.
141 sub makefont{
142     my ($requests, $suffix) = @_; # Receive REF for @requests array.
143     my $charstrings = "";
144     my $charnum=0;
145     my $encoding=""; # /Encoding vector
146     my $blackbox=<<'BLACKBOX';
147     {
148         0 1000 hsbw
149          100 800 rmoveto
150          -800 vlineto
151          800 hlineto
152          800 vlineto
153          closepath
154          endchar} |-
155 BLACKBOX
156
157      foreach my $req (@$requests){
158          # Note:
159          # Referene passing destroys the original array!
160          my $request="$req.$suffix";
161          my ($svg, $charstring);
162          my $char = sprintf("ch%03d", $charnum);
163          if(defined $db && $db->db_get($request, $svg)==0){
164              # If glyph is already in DB, then use it.
165              $charstring = svg2charstring($svg);
166              print STDERR "Use cached glyph for $request.\n";
167          }else{
168              # If glyph is not yet in DB, query KAGE server.
169              my $kageserver;
170              my $location; # For redirection
171              if($kageserver = IO::Socket::INET->new("$kageaddr")){
172                  print $kageserver "HEAD /$request.svg HTTP/1.1\r\n";
173                  print $kageserver "Host: $kageaddr\r\n\r\n";
174 #                #Get redirection info.
175 #                local $/="\r\n";
176 #                while(<$kageserver>){
177 #                    chomp;
178 #                    next unless m|^location:\s+http://([a-z0-9.:]+)/|i;
179 #                    $location=$1;
180 #                }
181 #                close($kageserver);
182 #                if($location
183 #                   and $kageserver=IO::Socket::INET->new($location)){
184 #                    print $kageserver "GET /$request.svg HTTP/1.1\r\n";
185 #                    print $kageserver "Host: $location\r\n\r\n";
186                      local $/; $svg=<$kageserver>;
187                      close($kageserver);
188                      $svg =~ s/\r//gm; # remove CR.
189                      $svg =~ s/^.+\n\n//ms; # remove HTTP header.
190 #                }else{
191 #                    $svg="error";
192 #                }
193              }else{
194                  print STDERR "Cannot connect to KAGE server at $kageaddr.\n";
195                  $svg="error";
196              }
197              if($charstring = svg2charstring($svg)){
198                  if(defined $db && $db->db_put($request, $svg)==0){
199                      # If glyph request is successful, then store it to DB.
200                      print STDERR "Glyph for $request cached.\n";
201                  }
202              }else{
203                  # If glyph request failed, then print a black box.
204                  # Do not store glyph to DB.
205                  print STDERR "Glyph request for $request failed.\n";
206                  $charstring = $blackbox;
207              }
208          }
209          $charstrings.= "/$char $charstring";
210          $encoding.= "dup $charnum/$char put\n";
211          $charnum++;
212      }
213     return ($charstrings, $encoding, $charnum);
214 }
215
216 ##############
217 #### main ####
218 ##############
219
220 # Read ids data generated by inCHISE
221 my $idsdata="$omegadb_path/idsdata.pl";
222 my ($font_start, $ids_start, %ids);
223 if(-e $idsdata){
224     # "require" doesn't work well.
225     # I don't know why...
226     open (my $data, "<:utf8","$idsdata");
227     while(<$data>){
228         eval $_;
229     }
230     close($data);
231 }else{
232     die "Cannot read $idsdata\n";
233 }
234
235 # Create (a nested) list of requests.
236 # $Request[font-number][char-code]='uhhhh...'
237 my @Requests;
238 foreach my $key (keys %ids){
239     my $code="";
240     my @elements=();
241     foreach my $elem (split(//,$key)){
242         $elem=unpack('U', $elem);
243         if($elem < 0x10000){ # BMP
244             $code.=sprintf("u%04x", $elem);
245         }elsif($elem < 0x110000){ # needs surrogate pair
246             my $high_surrogate=($elem-0x10000) / 0x400 + 0xd800;
247             my $low_surrogate=($elem-0x10000) % 0x400 + 0xdc00;
248             $code.=sprintf("u%04xu%04x",
249                            $high_surrogate, $low_surrogate);
250         }else{ #out of range.
251             $code="uffff"; # this generates KAGE server error.
252             last;
253         }
254     }
255     $Requests[$ids{$key}[0]]->[$ids{$key}[1]]=$code;
256 }
257
258 # Create fonts.
259 foreach my $fontnum (0 .. $#Requests){
260     my @faces=(['Mincho', 'min', 'mincho'],
261                ['Gothic', 'got', 'gothic']
262                );
263     foreach my $i (0 .. 1){
264         my $fontname=sprintf("Chise%s%03d", $faces[$i][0], $fontnum);
265         my $pfbname=sprintf("chisesub%03d%s.pfb", $fontnum, $faces[$i][1]);
266         my $suffix=$faces[$i][2]; # .mincho or .gothic
267         # Unique ID:
268         # mincho= 430000...,  gothic 440000...
269         my $unique_id=430000+$fontnum+$i*10000;
270
271         my($charstrings, $encoding, $charnum)=
272             makefont($Requests[$fontnum], $suffix);
273         
274         $encoding = sprintf("/Encoding %d array\n%sreadonly def",
275                             $charnum, $encoding);
276
277         my $font =
278             printheader($fontname, # FontName
279                         $fontname, # FullName
280                         'Chise',   # FamilyName
281                         'Medium',  # Weight
282                         '001.001', # version (whatever)
283                         $unique_id, # UniqueID (should be >= 4000000)
284                         $charnum+1, # Number of chars (.notdef included)
285                         $encoding   # Encoding vector
286                         ) . $charstrings . printfooter();
287         # Convert font to PFB
288         open(my $asm, '|-', "$t1asm -b -o $pfbname");
289         print $asm $font;
290         close($asm);
291         # Clean up PFB
292         system("$pfaedit -script $cleanup_script $pfbname");
293     }
294 }
295
296 # Close Database
297 undef $db;