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