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