outstanding change
[librarian.git] / librarian / font-optimizer / ext / Font-TTF / lib / Font / TTF / Name.pm
1 package Font::TTF::Name;
2
3 =head1 NAME
4
5 Font::TTF::Name - String table for a TTF font
6
7 =head1 DESCRIPTION
8
9 Strings are held by number, platform, encoding and language. Strings are
10 accessed as:
11
12     $f->{'name'}{'strings'}[$number][$platform_id][$encoding_id]{$language_id}
13
14 Notice that the language is held in an associative array due to its sparse
15 nature on some platforms such as Microsoft ($pid = 3). Notice also that the
16 array order is different from the stored array order (platform, encoding,
17 language, number) to allow for easy manipulation of strings by number (which is
18 what I guess most people will want to do).
19
20 By default, C<$Font::TTF::Name::utf8> is set to 1, and strings will be stored as UTF8 wherever
21 possible. The method C<is_utf8> can be used to find out if a string in a particular
22 platform and encoding will be returned as UTF8. Unicode strings are always
23 converted if utf8 is requested. Otherwise, strings are stored according to platform:
24
25 You now have to set <$Font::TTF::Name::utf8> to 0 to get the old behaviour.
26
27 =over 4
28
29 =item Apple Unicode (platform id = 0)
30
31 Data is stored as network ordered UCS2. There is no encoding id for this platform
32 but there are language ids as per Mac language ids.
33
34 =item Mac (platform id = 1)
35
36 Data is stored as 8-bit binary data, leaving the interpretation to the user
37 according to encoding id.
38
39 =item Unicode (platform id = 2)
40
41 Currently stored as 16-bit network ordered UCS2. Upon release of Perl 5.005 this
42 will change to utf8 assuming current UCS2 semantics for all encoding ids.
43
44 =item Windows (platform id = 3)
45
46 As per Unicode, the data is currently stored as 16-bit network ordered UCS2. Upon
47 release of Perl 5.005 this will change to utf8 assuming current UCS2 semantics for
48 all encoding ids.
49
50 =back
51
52 =head1 INSTANCE VARIABLES
53
54 =over 4
55
56 =item strings
57
58 An array of arrays, etc.
59
60 =back
61
62 =head1 METHODS
63
64 =cut
65
66 use strict;
67 use vars qw(@ISA $VERSION @apple_encs @apple_encodings $utf8 $cp_1252 @cp_1252 %win_langs %langs_win %langs_mac @ms_langids @mac_langs);
68 use Font::TTF::Table;
69 use Font::TTF::Utils;
70 @ISA = qw(Font::TTF::Table);
71
72 $utf8 = 1;
73
74 {
75     my ($count, $i);
76     eval {require Compress::Zlib;};
77     unless ($@)
78     {
79         for ($i = 0; $i <= $#apple_encs; $i++)
80         {
81             $apple_encodings[0][$i] = [unpack("n*", Compress::Zlib::uncompress(unpack("u", $apple_encs[$i])))]
82                 if (defined $apple_encs[$i]);
83             foreach (0 .. 127)
84             { $apple_encodings[0][$i][$_] = $_; }
85             $count = 0;
86             $apple_encodings[1][$i] = {map {$_ => $count++} @{$apple_encodings[0][$i]}};
87         }
88         $cp_1252[0] = [unpack("n*", Compress::Zlib::uncompress(unpack("u", $cp_1252)))];
89         $count = 0;
90         $cp_1252[1] = {map({$_ => $count++} @{$cp_1252[0]})};
91     }
92     for ($i = 0; $i < $#ms_langids; $i++)
93     {
94         if (defined $ms_langids[$i][1])
95         {
96             my ($j);
97             for ($j = 0; $j < $#{$ms_langids[$i][1]}; $j++)
98             {
99                 my ($v) = $ms_langids[$i][1][$j];
100                 if ($v =~ m/^-/o)
101                 { $win_langs{(($j + 1) << 10) + $i} = $ms_langids[$i][0] . $v; }
102                 else
103                 { $win_langs{(($j + 1) << 10) + $i} = $v; }
104             }
105         }
106         else
107         { $win_langs{$i + 0x400} = $ms_langids[$i][0]; }
108     }
109     %langs_win = map {my ($t) = $win_langs{$_}; my (@res) = ($t => $_); push (@res, $t => $_) if ($t =~ s/-.*$//o && ($_ & 0xFC00) == 0x400); @res} keys %win_langs;
110     $i = 0;
111     %langs_mac = map {$_ => $i++} @mac_langs;
112 }
113     
114
115 $VERSION = 1.1;             # MJPH  17-JUN-2000     Add utf8 support
116 # $VERSION = 1.001;           # MJPH  10-AUG-1998     Put $number first in list
117
118 =head2 $t->read
119
120 Reads all the names into memory
121
122 =cut
123
124 sub read
125 {
126     my ($self) = @_;
127     my ($fh) = $self->{' INFILE'};
128     my ($dat, $num, $stroff, $i, $pid, $eid, $lid, $nid, $len, $off, $here);
129
130     $self->SUPER::read or return $self;
131     $fh->read($dat, 6);
132     ($num, $stroff) = unpack("x2nn", $dat);
133     for ($i = 0; $i < $num; $i++)
134     {
135         use bytes;              # hack to fix bugs in 5.8.7
136         read($fh, $dat, 12);
137         ($pid, $eid, $lid, $nid, $len, $off) = unpack("n6", $dat);
138         $here = $fh->tell();
139         $fh->seek($self->{' OFFSET'} + $stroff + $off, 0);
140         $fh->read($dat, $len);
141         if ($utf8)
142         {
143             if ($pid == 1 && defined $apple_encodings[0][$eid])
144             { $dat = TTF_word_utf8(pack("n*", map({$apple_encodings[0][$eid][$_]} unpack("C*", $dat)))); }
145             elsif ($pid == 2 && $eid == 2 && @cp_1252)
146             { $dat = TTF_word_utf8(pack("n*", map({$cp_1252[0][$_]} unpack("C*", $dat)))); }
147             elsif ($pid == 0 || $pid == 3 || ($pid == 2 && $eid == 1))
148             { $dat = TTF_word_utf8($dat); }
149         }
150         $self->{'strings'}[$nid][$pid][$eid]{$lid} = $dat;
151         $fh->seek($here, 0);
152     }
153     $self;
154 }
155
156
157 =head2 $t->out($fh)
158
159 Writes out all the strings
160
161 =cut
162
163 sub out
164 {
165     my ($self, $fh) = @_;
166     my ($pid, $eid, $lid, $nid, $todo, @todo);
167     my ($len, $offset, $loc, $stroff, $endloc, $str_trans);
168
169     return $self->SUPER::out($fh) unless $self->{' read'};
170
171     $loc = $fh->tell();
172     $fh->print(pack("n3", 0, 0, 0));
173     foreach $nid (0 .. $#{$self->{'strings'}})
174     {
175         foreach $pid (0 .. $#{$self->{'strings'}[$nid]})
176         {
177             foreach $eid (0 .. $#{$self->{'strings'}[$nid][$pid]})
178             {
179                 foreach $lid (sort keys %{$self->{'strings'}[$nid][$pid][$eid]})
180                 {
181                     $str_trans = $self->{'strings'}[$nid][$pid][$eid]{$lid};
182                     if ($utf8)
183                     {
184                         if ($pid == 1 && defined $apple_encodings[1][$eid])
185                         { $str_trans = pack("C*",
186                                 map({$apple_encodings[1][$eid]{$_} || 0x3F} unpack("n*",
187                                 TTF_utf8_word($str_trans)))); }
188                         elsif ($pid == 2 && $eid == 2 && @cp_1252)
189                         { $str_trans = pack("C*",
190                                 map({$cp_1252[1][$eid]{$_} || 0x3F} unpack("n*",
191                                 TTF_utf8_word($str_trans)))); }
192                         elsif ($pid == 2 && $eid == 0)
193                         { $str_trans =~ s/[\xc0-\xff][\x80-\xbf]+/?/og; }
194                         elsif ($pid == 0 || $pid == 3 || ($pid == 2 && $eid == 1))
195                         { $str_trans = TTF_utf8_word($str_trans); }
196                     }
197                     push (@todo, [$pid, $eid, $lid, $nid, $str_trans]);
198                 }
199             }
200         }
201     }
202
203     $offset = 0;
204     @todo = (sort {$a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2]
205             || $a->[3] <=> $b->[3]} @todo);
206     foreach $todo (@todo)
207     {
208         $len = length($todo->[4]);
209         $fh->print(pack("n6", @{$todo}[0..3], $len, $offset));
210         $offset += $len;
211     }
212     
213     $stroff = $fh->tell() - $loc;
214     foreach $todo (@todo)
215     { $fh->print($todo->[4]); }
216
217     $endloc = $fh->tell();
218     $fh->seek($loc, 0);
219     $fh->print(pack("n3", 0, $#todo + 1, $stroff));
220     $fh->seek($endloc, 0);
221     $self;
222 }
223
224
225 =head2 $t->XML_element($context, $depth, $key, $value)
226
227 Outputs the string element in nice XML (which is all the table really!)
228
229 =cut
230
231 sub XML_element
232 {
233     my ($self) = shift;
234     my ($context, $depth, $key, $value) = @_;
235     my ($fh) = $context->{'fh'};
236     my ($nid, $pid, $eid, $lid);
237
238     return $self->SUPER::XML_element(@_) unless ($key eq 'strings');
239
240     foreach $nid (0 .. $#{$self->{'strings'}})
241     {
242         next unless ref($self->{'strings'}[$nid]);
243 #        $fh->print("$depth<strings id='$nid'>\n");
244         foreach $pid (0 .. $#{$self->{'strings'}[$nid]})
245         {
246             foreach $eid (0 .. $#{$self->{'strings'}[$nid][$pid]})
247             {
248                 foreach $lid (sort {$a <=> $b} keys %{$self->{'strings'}[$nid][$pid][$eid]})
249                 {
250                     my ($lang) = $self->get_lang($pid, $lid) || $lid;
251                     $fh->printf("%s<string id='%s' platform='%s' encoding='%s' language='%s'>\n%s%s%s\n%s</string>\n",
252                             $depth, $nid, $pid, $eid, $lang, $depth,
253                             $context->{'indent'}, $self->{'strings'}[$nid][$pid][$eid]{$lid}, $depth);
254                 }
255             }
256         }
257 #        $fh->print("$depth</strings>\n");
258     }
259     $self;
260 }
261
262
263 =head2 $t->XML_end($context, $tag, %attrs)
264
265 Store strings in the right place
266
267 =cut
268
269 sub XML_end
270 {
271     my ($self) = shift;
272     my ($context, $tag, %attrs) = @_;
273
274     if ($tag eq 'string')
275     {
276         my ($lid) = $self->find_name($attrs{'platform'}, $attrs{'language'}) || $attrs{'language'};
277         $self->{'strings'}[$attrs{'id'}][$attrs{'platform'}][$attrs{'encoding'}]{$lid}
278             = $context->{'text'};
279         return $context;
280     }
281     else
282     { return $self->SUPER::XML_end(@_); }
283 }
284
285 =head2 is_utf8($pid, $eid)
286
287 Returns whether a string of a given platform and encoding is going to be in UTF8
288
289 =cut
290
291 sub is_utf8
292 {
293     my ($self, $pid, $eid) = @_;
294
295     return ($utf8 && ($pid == 0 || $pid == 3 || ($pid == 2 && ($eid != 2 || @cp_1252))
296             || ($pid == 1 && defined $apple_encodings[$eid])));
297 }
298
299
300 =head2 find_name($nid)
301
302 Hunts down a name in all the standard places and returns the string and for an
303 array context the pid, eid & lid as well
304
305 =cut
306
307 sub find_name
308 {
309     my ($self, $nid) = @_;
310     my ($res, $pid, $eid, $lid, $look, $k);
311
312     my (@lookup) = ([3, 1, 1033], [3, 1, -1], [3, 0, 1033], [3, 0, -1], [2, 1, -1], [2, 2, -1], [2, 0, -1],
313                     [0, 0, 0], [1, 0, 0]);
314     foreach $look (@lookup)
315     {
316         ($pid, $eid, $lid) = @$look;
317         if ($lid == -1)
318         {
319             foreach $k (keys %{$self->{'strings'}[$nid][$pid][$eid]})
320             {
321                 if (($res = $self->{strings}[$nid][$pid][$eid]{$k}) ne '')
322                 {
323                     $lid = $k;
324                     last;
325                 }
326             }
327         } else
328         { $res = $self->{strings}[$nid][$pid][$eid]{$lid} }
329         if ($res ne '')
330         { return wantarray ? ($res, $pid, $eid, $lid) : $res; }
331     }
332     return '';
333 }
334
335
336 =head2 set_name($nid, $str[, $lang[, @cover]])
337
338 Sets the given name id string to $str for all platforms and encodings that
339 this module can handle. If $lang is set, it is interpretted as a language
340 tag and if the particular language of a string is found to match, then
341 that string is changed, otherwise no change occurs.
342
343 If supplied, @cover should be a list of references to two-element arrays 
344 containing pid,eid pairs that should added to the name table if not already present.
345
346 This function does not add any names to the table unless @cover is supplied. 
347
348 =cut
349
350 sub set_name
351 {
352     my ($self, $nid, $str, $lang, @cover) = @_;
353     my ($pid, $eid, $lid, $c);
354
355     foreach $pid (0 .. $#{$self->{'strings'}[$nid]})
356     {
357         my $strNL = $str;
358         $strNL =~ s/\n/\r\n/og  if $pid == 3;
359         $strNL =~ s/\n/\r/og    if $pid == 1;
360         foreach $eid (0 .. $#{$self->{'strings'}[$nid][$pid]})
361         {
362             foreach $lid (keys %{$self->{'strings'}[$nid][$pid][$eid]})
363             {
364                 next unless (!defined $lang || $self->match_lang($pid, $lid, $lang));
365                 $self->{'strings'}[$nid][$pid][$eid]{$lid} = $strNL;
366                 foreach $c (0 .. scalar @cover)
367                 {
368                     next unless ($cover[$c][0] == $pid && $cover[$c][1] == $eid);
369                     delete $cover[$c];
370                     last;
371                 }
372             }
373         }
374     }
375     foreach $c (@cover)
376     {
377         my ($pid, $eid) = @{$c};
378         my ($lid) = $self->find_lang($pid, $lang);
379         my $strNL = $str;
380         $strNL =~ s/\n/\r\n/og  if $pid == 3;
381         $strNL =~ s/\n/\r/og    if $pid == 1;
382         $self->{'strings'}[$nid][$pid][$eid]{$lid} = $strNL;
383     }
384     return $self;
385 }
386
387 =head2 Font::TTF::Name->match_lang($pid, $lid, $lang)
388
389 Compares the language associated to the string of given platform and language
390 with the given language tag. If the language matches the tag (i.e. is equal
391 or more defined than the given language tag) returns true. This is calculated
392 by finding whether the associated language tag starts with the given language
393 tag.
394
395 =cut
396
397 sub match_lang
398 {
399     my ($self, $pid, $lid, $lang) = @_;
400     my ($langid) = $self->get_lang($pid, $lid);
401
402     return ($lid == $lang) if ($lang != 0 || $lang eq '0');
403     return !index(lc($langid), lc($lang));
404 }
405
406 =head2 Font::TTF::Name->get_lang($pid, $lid)
407
408 Returns the language tag associated with a particular platform and language id
409
410 =cut
411
412 sub get_lang
413 {
414     my ($self, $pid, $lid) = @_;
415
416     if ($pid == 3)
417     { return $win_langs{$lid}; }
418     elsif ($pid == 1)
419     { return $mac_langs[$lid]; }
420     return '';
421 }
422
423
424 =head2 Font::TTF::Name->find_lang($pid, $lang)
425
426 Looks up the language name and returns a lang id if one exists
427
428 =cut
429
430 sub find_lang
431 {
432     my ($self, $pid, $lang) = @_;
433
434     if ($pid == 3)
435     { return $langs_win{$lang}; }
436     elsif ($pid == 1)
437     { return $langs_mac{$lang}; }
438     return undef;
439 }
440
441
442 BEGIN {
443 @apple_encs = (
444 <<'EOT',
445 M>)RES==NCW$`@.'G_S5Q*L(!#?+K1VO4:.W6IJA-:\^BM?>L>1&NP(A0Q$BL
446 M<*!62ZV8Z1)[K]BE$MR#O,=/7OW]7T&*6"NMI4K31EOMM)>N@XXZZ2Q#IBZZ
447 MZJ:['GKJ)4NVWOKHJ]\_/\!`@PR68XBAALDUW`@CC3+:&&.-,UZ>?!-,-,ED
448 M4TPUS70SS#3+;`7FF&N>0D7F6V"A119;8JEEEEMAI5566V.M==;;H-A&FVRV
449 MQ5;;_OTONJ3<%;?<5^NQ1YYXYJGG7GKME3?>>N^=#S[ZY(O/OOKNFU]^JO<[
450 M!$?LLMO>$#OAH4-*4F+'[(L+E*F,6SH:%\9%]C@>1W&CN&%2:9QNO]-))5ZH
451 M<]9.!^/DQ/8X-V[@@#,AS0ZE+KB7R$ODA\:A26@>6H2FH9D?J17^)(I#3C@8
452 MLD)V?:(^"BE.AN30,F0XK\(Y5UUVW0TW77/'W;H_;JM6HRJ1&95%M0Y'E5%5
453 .5.U4]""JB<K_`B>`?E$`
454 EOT
455
456 undef,
457 undef,
458 undef,
459 <<'EOT',
460 M>)RES[=/%```1O$WO8G_@$'J';W70Z2WHS>5WJN%8D6%D;BZ,3*P,;#C2D(8
461 M,9&)08V)+4*(1((X2'(#[.:;7[[\*./_%D,L<<230"(!@B213`JII)%.!IED
462 MD4T.N>213P&%%%%,B!)N4LJMR[Z<"BJIHIH::JFCG@;"--)$,RVTTD8['732
463 M13>WN<-=>NBECWX&&&2(848898QQ)IADBFEFF.4>]WG`0^:89X%%'O&8)SSE
464 M&<]9X@4O><4R*Y?_.ZRSRQ[[''#(1S[PB<]NL\D7OO&5[_S@9TR`(XXYX1=O
465 M.>4W9_SAG`O^7OF=O>XW*N)WV!%''7/<"2>=<MH90D9'_-X(AHTUSG@33#1@
466 MT"2333'5--/-,-,LL\TQUSSS+;#0(HL-7?DMM\)*JZRVQEKKK+?!L(TVV6R+
467 9K;;9;H<K+KGJ>S?<\K5O(G[7?/</+>Y>'```
468 EOT
469
470 <<'EOT',
471 M>)RED$LSEW$`A9_-^00L,H-^(=>4Y%^2J'1Q*Y+[I2(BHA`B?!%J6EM1*28S
472 M;9II[/PI*7*_%TUN\_*VZ%W:FN9LSYEGGD,\_Q?#$?SP)X"C!!)$,"&$$L8Q
473 MPCG."2(X222GB,+%:<X0S5EB.$<LYXES]A>XR"42N,P5KG*-1))()H54KG.#
474 M--*Y20:WR"2+;'+()8]\"BBDB-O<X2[%E'"/4LJX3SD5/*"2*AY230V/>$PM
475 M==3SA`8::>(IS;3PC%;::'?X'^W#?&(0-Z-,,,,TL\PSQP)+K+#,*C]9XQ?K
476 M_.8/FVRPQ0[;[+&+S=_]_J;KX/Y6I?&U.JQ.Z[GU0@-VBNTR@;Q4G]ZI5V_U
477 MQG@83^-M?,PAXV6'VF'ZH&Z]4H_>J]]IO=:0W!K6B#[KBT;U56/ZIN\:UX1^
478 ?:%)3FM:,9C6G>2UH44M:UHI6'?<BYX,"6O\!%-%\5```
479 EOT
480
481 <<'EOT',
482 M>)RES5=OSG$`0.$CYR.(A(3DUS]J4WOO59O6;&F+UMY[7R&(V'N^4ETZ=*"J
483 M:M:H=>E*0D1B)7HC1KC0[R#G^LEA,/]7((Z(EK2B-?&TH2WM:$\'.M*)SG0A
484 M@:YTHSL]Z$DO>M.'OO2C/P,8R*`&/X2A#&,X(QC)*$:3R!C&,H[Q3&`BDYC,
485 M%))(9BK3F,X,9C*+%%*9S1S22">#N<QC/IEDL8"%+&(Q2UC*,I:S@I6L8C5K
486 M6,LZUK.!C6QB,UO8RC:VLZ/A7TL5Y=11P6O>N(MWO.>#.\GG(Y_YQ!>^DAT7
487 M\8WZ$%$3$OC.#W(IYC=_^!N"1SWF*<]ZP1AO*:'`;*^0%V502J6'*8LRHRQR
488 M/.)Q3WC2TY[QG+D6FF^!19ZGR(M>BA*]3"'5(9Z8.>:YVSV-DD/CT"0T#RU"
489 MT]",G^YUG_L]8+$E7O6%!WUIF>4^]9K7?6R%E59YQUM6>]L:[WK/5][WH;7>
490 4M,X'/O&1-WSF<P]9^BOV#YW%>_\`
491 EOT
492
493 <<'EOT',
494 M>)RERT=.%5``0-&+7K'&!B(@X/L/^/3>ZZ?SZ=*K@`KVWOL:U!68.#!&8G2@
495 M$Q?F5/=@SOB0XO\$$D2**:&4)&644T$E55130RUUU--`(TTTTT(K;;3302==
496 M=--#[[_?1S\###+$,".,DF:,<2:89(II9KC`+'/,L\`B2RRSPBIKK+/!13;9
497 M8IM+7.8*.^QRE6M<YP8WN<5M[G"7>]SG`0]YQ&.>\)1G/.<%+WG%:][PEI0G
498 M/>5IL\SVC#F>-=<\\SUG@846>=Y@PFBQ)9::M,QR*ZRTRFIKK+4N!+[[CD]\
499 M#I%?9O*-+XGH/N?BMON=CT7\B#MQUR5^^MY#ZH('7?:PJQYQS14/L!?S,S[$
500 M=,SD*[]#DH\>==UC;K@8LD)V*`B%(3?D\2<4>=Q-3[B5R#'#66>LM\%&FVRV
501 GQ5;;;+?#3KOLML=>4_;9[X"##CGLB*.F'7/<"2>=<CKL_06V`DD#
502 EOT
503
504 undef,
505 <<'EOT',
506 M>)RED-DVUG$`1;=:U*Y%0C)5O^^/SSS/F>>9#"$JE7D>"D6\3S=>Q^MPU^JF
507 M&^M<G[7//G1ROP1B1.130"%QBBBFA%+***>"2JJHIH9:ZJBG@4:::*:%M[32
508 M1CL==_TNNNFAES[Z&6"0(889890QQIE@DG=,,<T,L[QGCGD6^,`B2WSD$Y]9
509 MY@M?^<8*JZRQS@:;;+'-#KOLL<\!AQQQS'=^<,(I9_SD%^=<\)M+KN[X-U%:
510 M2`\9(2MDAWB(^,-U+/KKYYJ'_W_`!!_XT$23?.1C]8E/3?&9J2:;9KH9/O>%
511 MF;XTRVQSS#7/5[[VC<&8D?D66&C<(HLML=0RRZVPTBJ7K;;&6NNLM\%&FVRV
512 L):388:===MMCKP,..F2_(XXZYK#CMKGZS[YU-]QTRVUWW'7/?0]N`4(?0WT`
513 EOT
514
515 <<'EOT',
516 M>)RED,5.0U$415=(D.X!$"ANMX^VN+M#D>+N[H4"Q5W^APF_PZ\PY.9-"`-&
517 MY.3LG>-"#_\3@P^'8OP$"%)"*6644T$E55130RUUU--`(TTTTT(K;;3302==
518 M=-OZ7OH(T<\`@PP19I@11AECG`DFF6*:&6:98YX%%EEBF15666.=#3;98IL=
519 M=MECGP,.B7#$,5%...6,&.=<<,D5U]QPRQWW//#($\^\\,J;G?_II)ETXS79
520 M)L<$C<,['S[GYSY=?FWK6E>Z^?L'BK,:KP0E*DD>R?6E*-7E='DM9BA36<I6
521 MCG*5IWP5J%!%,O+)4;'\"BBH$I7:S')5J%)5JE:-M6JMUKM]FM1LL55M)EG=
522 GZE&O^A1R(V$-NSRF<8L3ZO3L_]KN4!$=Z5A1G>A49XKI_!M<9D8J
523 EOT
524
525 <<'EOT',
526 M>)RED,E3SW$8QU_77@<''+A]^Y5(2-F7+"%92\B^ES5ES]H,)L(8&21E*UNH
527 M&"8T8ZS3I(FS_T"$_`L^-^/D8)YY/^]Y/\L\"Y/Y/XN()T8"B0P@B8$,(IG!
528 MI#"$H0PCE>&DD<X(1C**T8QA+.,8SP0FDL&DT#^%J60RC>G,((N99#.+V<QA
529 M+O.83PZY+""/A2QB,?DL82G+6,X*5K**U:QA+>M8SP8**&0CF]C,%K:RC2*V
530 M4TP).]C)+G:SA[WLHY3]'.`@ASC,$<K"_,^QWE&?J&_4+^H?)44Q[M,<'_MS
531 M7USAOS[@48]YW')/>-(*3WG:,R%ZSDK/!K[@1<][R2HO6^T5:ZSUJM>\[@UO
532 M6F>]M[SM'>]ZSX90_\"'-MIDLX^">ASPQ*?!M_C,Y[ZP->KE*U_[QK>^\WW(
533 CM/O!ML"=?K3#3[Z,*_AKOR]V^=5O=OO='_ZTQU^_`2-%:*``
534 EOT
535
536 undef,
537 undef,
538 undef,
539 undef,
540 undef,
541 undef,
542 undef,
543 undef,
544 undef,
545 <<'EOT',
546 M>)REC]=.E&$`1(\%&W@4004%_7:!I?>.Z-+[TJL*=K"`BH`*J,_"+2'A!7PW
547 MX;\2[LG<3#*9G!F2G$V!&'$***2(!,644$H9Y5102175U%!+'?4TT$@3S;30
548 M2AN/:.<Q3Z)^!YUTT4T/O?31SP"###',""E&&6.<"2:98IH99IECG@6>\HSG
549 M+++$"U[RBM>\X2WO6&:%]WS@(Y]898W/?.$KZWQC@TVV^,X/?K+-#KO\XC=_
550 M(OX!?T/"`0<=<MB1$Q?R0KXIDB%NK?TVV&B3S:?RG)`;]?<\YWDO>-$T+WG9
551 M*U[UFNEF>%V]X4TSO666V=[VCG?-,==[WC?/?!_XT&#,N`466F3"8DLLM<QR
552 M*ZRTRFIK(GJ=]?_Y+;;:]N\HI(>LD&W2#COMLML>>^V+=IX\2<7BCCGNA)-.
553 0.>V,L\XY[P*'[!\#D^='L@``
554 EOT
555
556 undef,
557 undef,
558 undef,
559 undef,
560 undef,
561 undef,
562 undef,
563 undef,
564 undef,
565 );
566
567 $cp_1252 = (
568 <<'EOT',
569 M>)P-SD-B'5```,#YJ6VE>DEM&[\VD]JVF?H./4'-U+93V[9M:SV;$141(Y74
570 MTD@KG?0RR"B3S++(*IOL<L@IE]SRR"N?_`J(55`AA1515!`G7C'%E5!2*:65
571 M458YY550426555%5-=754%,MM=515SWU-=!05".--=%4,\VUT%(KK;715COM
572 M==!1)YTE2-1%5]UTUT-/O?361U_]]#?`0(,,-L10PPPWPDBCC#;&6..,-\%$
573 MDTPVQ5333)=DAIEFF6V.N>:%9-$0&YD?BH22(82XF)10.3(@U(DDB$;F_/]%
574 M0_Y0(!0*A4-\R!5RQ]R*BX\,#'4CB?]];B3)`@LMLM@22RVSW`HKK;):LC76
575 M6F>]#3;:9+,MMMIFNQUVVF6W/?;:9[\##CKDL"-2''7,<2><=,II9YQUSGD7
576 M7'3)95=<=<UU-]QTRVUWW'7/?0\\],AC3SSUS',OO/3*:V^\]<Y['WSTR6=?
577 1?/7-=S_\],MO?_S]!Y==>0@`
578 EOT
579 );
580 #'
581
582 @ms_langids = ( [""],
583     ['ar', ["-SA", "-IQ", "-EG", "-LY", "-DZ", "-MA", "-TN", 
584             "-OM", "-YE", "-SY", "-JO", "-LB", "-KW", "-AE",
585             "-BH", "-QA"]],
586     ['bg-BG'],
587     ['ca-ES'],
588     ['zh', ['-TW', 'CN', '-HK', '-SG', '-MO']],
589     ["cs-CZ"],
590     ["da-DK"],
591     ["de", ["-DE", "-CH", "-AT", "-LU", "-LI"]],
592     ["el-GR"],
593     ["en", ["-US", "-UK", "-AU", "-CA", "-NZ", "-IE", "-ZA",
594             "-JM", "029", "-BZ", "-TT", "-ZW", "-PH", "-ID",
595             "-HK", "-IN", "-MY", "-SG"]],
596     ["es", ["-ES", "-MX", "-ES", "-GT", "-CR", "-PA", "-DO",
597             "-VE", "-CO", "-PE", "-AR", "-EC", "-CL", "-UY",
598             "-PY", "-BO", "-SV", "-HN", "-NI", "-PR", "-US"]],
599     ["fi-FI"],
600     ["fr", ["-FR", "-BE", "-CA", "-CH", "-LU", "-MC", "",
601             "-RE", "-CG", "-SN", "-CM", "-CI", "-ML", "-MA",
602             "-HT"]],
603     ["he-IL"],
604     ["hu-HU"],
605     ["is-IS"],
606 # 0010
607     ["it", ["-IT", "-CH"]],
608     ["ja-JP"],
609     ["ko-KR"],
610     ["nl", ["-NL", "-BE"]],
611     ["no", ["-bok-NO", "-nyn-NO"]],
612     ["pl-PL"],
613     ["pt", ["-BR", "-PT"]],
614     ["rm-CH"],
615     ["ro", ["-RO", "_MD"]],
616     ["ru-RU"],
617     ["hr", ["-HR", "-Latn-CS", "Cyrl-CS", "-BA", "", "-Latn-BA", "-Cyrl-BA"]],
618     ["sk-SK"],
619     ["sq-AL"],
620     ["sv", ["-SE", "-FI"]],
621     ["th-TH"],
622     ["tr-TR"],
623 # 0020
624     ["ur", ["-PK", "tr-IN"]],
625     ["id-ID"],
626     ["uk-UA"],
627     ["be-BY"],
628     ["sl-SL"],
629     ["et-EE"],
630     ["lv-LV"],
631     ["lt-LT"],
632     ["tg-Cyrl-TJ"],
633     ["fa-IR"],
634     ["vi-VN"],
635     ["hy-AM"],
636     ["az", ["-Latn-AZ", "-Cyrl-AZ"]],
637     ["eu-ES"],
638     ["wen". ["wen-DE", "dsb-DE"]],
639     ["mk-MK"],
640 # 0030
641     ["st"],
642     ["ts"],
643     ["tn-ZA"],
644     ["ven"],
645     ["xh-ZA"],
646     ["zu-ZA"],
647     ["af-ZA"],
648     ["ka-GE"],
649     ["fo-FO"],
650     ["hi-IN"],
651     ["mt"],
652     ["se", ["-NO", "-SE", "-FI", "smj-NO", "smj-SE", "sma-NO", "sma-SE",
653             "", "smn-FI"]],
654     ["ga-IE"],
655     ["yi"],
656     ["ms", ["-MY", "-BN"]],
657     ["kk-KZ"],
658 # 0040
659     ["ky-KG"],
660     ["sw-KE"],
661     ["tk-TM"],
662     ["uz", ["-Latn-UZ", "-Cyrl-UZ"]],
663     ["tt-RU"],
664     ["bn", ["-IN", "-BD"]],
665     ["pa", ["-IN", "-Arab-PK"]],
666     ["gu-IN"],
667     ["or-IN"],
668     ["ta-IN"],
669     ["te-IN"],
670     ["kn-IN"],
671     ["ml-IN"],
672     ["as-IN"],
673     ["mr-IN"],
674     ["sa-IN"],
675 # 0050
676     ["mn", ["-Cyrl-MN", "-Mong-CN"]],
677     ["bo", ["-CN", "-BT"]],
678     ["cy-GB"],
679     ["km-KH"],
680     ["lo-LA"],
681     ["my"],
682     ["gl-ES"],
683     ["kok-IN"],
684     ["mni"],
685     ["sd", ["-IN", "-PK"]],
686     ["syr-SY"],
687     ["si-LK"],
688     ["chr"],
689     ["iu", ["-Cans-CA", "-Latn-CA"]],
690     ["am-ET"],
691     ["tmz", ["-Arab", "tmz-Latn-DZ"]],
692 # 0060
693     ["ks"],
694     ["ne", ["-NP", "-IN"]],
695     ["fy-NL"],
696     ["ps-AF"],
697     ["fil-PH"],
698     ["dv-MV"],
699     ["bin-NG"],
700     ["fuv-NG"], 
701     ["ha-Latn-NG"],
702     ["ibb-NG"],
703     ["yo-NG"],
704     ["quz", ["-BO", "-EC", "-PE"]],
705     ["ns-ZA"],
706     ["ba-RU"],
707     ["lb-LU"],
708     ["kl-GL"],
709 # 0070
710     ["ig-NG"],
711     ["kau"],
712     ["om"],
713     ["ti", ["-ET". "-ER"]],
714     ["gn"],
715     ["haw"],
716     ["la"],
717     ["so"],
718     ["ii-CN"],
719     ["pap"],
720     ["arn-CL"],
721     [""],           # (unassigned)
722     ["moh-CA"],
723     [""],           # (unassigned)
724     ["br-FR"],
725     [""],           # (unassigned)
726 # 0080
727     ["ug-CN"],
728     [""],           # (unassigned)
729     ["oc-FR"],
730     ["gsw-FR"],
731     [""],           # (unassigned)
732     ["sah-RU"],
733     ["qut-GT"],
734     ["rw-RW"],
735     ["wo-SN"],
736     [""],           # (unassigned)
737     [""],           # (unassigned)
738     [""],           # (unassigned)
739     ["gbz-AF"],
740 );
741
742 @mac_langs = (
743     'en', 'fr', 'de', 'it', 'nl', 'sv', 'es', 'da', 'pt', 'no',
744     'he', 'ja', 'ar', 'fi', 'el', 'is', 'mt', 'tr', 'hr', 'zh-Hant',
745     'ur', 'hi', 'th', 'ko', 'lt', 'pl', 'hu', 'et', 'lv', 'se',
746     'fo', 'ru' ,'zh-Hans', 'nl', 'ga', 'sq', 'ro', 'cs', 'sk',
747     'sl', 'yi', 'sr', 'mk', 'bg', 'uk', 'be', 'uz', 'kk', 'az-Cyrl',
748     'az-Latn', 'hy', 'ka', 'mo', 'ky', 'abh', 'tuk', 'mn-Mong', 'mn-Cyrl', 'pst',
749     'ku', 'ks', 'sd', 'bo', 'ne', 'sa', 'mr', 'bn', 'as', 'gu',
750     'pa', 'or', 'ml', 'kn', 'ta', 'te', 'si', 'my', 'km', 'lo',
751     'vi', 'id', 'tl', 'ms-Latn', 'ms-Arab', 'am', 'ti', 'tga', 'so', 'sw',
752     'rw', 'rn', 'ny', 'mg', 'eo', '', '', '', '', '',
753     '', '', '', '', '', '', '', '', '', '',
754     '', '', '', '', '', '', '', '', '', '',
755     '', '', '', '', '', '', '', '', 'cy', 'eu',
756     'la', 'qu', 'gn', 'ay', 'tt', 'ug', 'dz', 'jv-Latn', 'su-Latn',
757     'gl', 'af', 'br', 'iu', 'gd', 'gv', 'gd-IR-x-dotabove', 'to', 'el-polyton', 'kl',
758     'az-Latn'
759 );
760
761 }
762
763 1;
764
765 =head1 BUGS
766
767 =over 4
768
769 =item *
770
771 Unicode type strings will be stored in utf8 for all known platforms,
772 once Perl 5.6 has been released and I can find all the mapping tables, etc.
773
774 =back
775
776 =head1 AUTHOR
777
778 Martin Hosken Martin_Hosken@sil.org. See L<Font::TTF::Font> for copyright and
779 licensing.
780
781 =cut
782