File Coverage

blib/lib/Font/FNT.pm
Criterion Covered Total %
statement 91 91 100.0
branch 3 6 50.0
condition n/a
subroutine 8 8 100.0
pod 5 5 100.0
total 107 110 97.2


line stmt bran cond sub pod time code
1             package Font::FNT;
2              
3             our $VERSION = '0.02';
4              
5 2     2   39134 use strict;
  2         4  
  2         68  
6 2     2   10 use warnings;
  2         3  
  2         61  
7 2     2   1682 use YAML();
  2         19969  
  2         2447  
8              
9             my @Spec = qw
10             (
11             n Version
12             L Size
13             Z60 Copyright
14             S Type
15             S Points
16             S VertRes
17             S HorizRes
18             S Ascent
19             S InternalLeading
20             S ExternalLeading
21             C Italic
22             C Underline
23             C StrikeOut
24             S Weight
25             C CharSet
26             S PixWidth
27             S PixHeight
28             C PitchAndFamily
29             S AvgWidth
30             S MaxWidth
31             C FirstChar
32             C LastChar
33             C DefaultChar
34             C BreakChar
35             S WidthBytes
36             L Device
37             L Face
38             L BitsPointer
39             L BitsOffset
40             Z1 Reserved
41             L Flags
42             S Aspace
43             S Bspace
44             S Cspace
45             L ColorPointer
46             Z16 Reserved1
47             );
48             my ( @k, @v );
49             for ( my $i = 0; $i < @Spec; $i += 2 )
50             {
51             push @k, $Spec[$i+1];
52             push @v, $Spec[$i+0];
53             }
54             # -----------------------------------------------------------------------------
55             sub load
56             # -----------------------------------------------------------------------------
57             {
58 1     1 1 575 my $class = shift;
59 1         2 my $File = shift;
60 1         3 my $self = {};
61              
62 1 50       35 open my $f, $File or die $!;
63 1         2 binmode $f;
64 1         6 local $/;
65 1         29 my $s = <$f>;
66 1         20 my @a = unpack "@v A*", $s;
67 1         3 my $Rest = pop @a;
68 1         28 @$self{@k} = @a;
69              
70 1         5 $self->{FaceName} = unpack "x$self->{Face} Z*", $s;
71 1         3 my $CharTableSize = $self->{LastChar} - $self->{FirstChar} + 2;
72 1         66 my @CharTable = unpack 'SL' x $CharTableSize, $Rest;
73 1         12 for ( 0 .. $CharTableSize - 2 )
74             {
75 256         309 my $Width = $CharTable[2*$_];
76 256         284 my $Offset = $CharTable[2*$_+1];
77 256         298 my $Bytes = $CharTable[2*$_+3] - $Offset;
78 256         565 my $Char = { Width => $Width, Code => $_ + $self->{FirstChar} };
79 256         394 $self->{Chars}[$_] = $Char;
80 256         893 my @Bmp = unpack "x$Offset C$Bytes", $s;
81 256         308 my @Cmp;
82 256         3872 $Cmp[$_ % $self->{PixHeight}] .= sprintf '%08b', $Bmp[$_] for 0 .. $#Bmp;
83 256         1367 $_ = substr $_, 0, $Width for @Cmp;
84 256         1020 tr/01/-#/ for @Cmp;
85 256         602 $Char->{BitMap} = \@Cmp;
86             }
87 1         40 bless $self, $class;
88             }
89             # -----------------------------------------------------------------------------
90             sub save
91             # -----------------------------------------------------------------------------
92             {
93 1     1 1 368 my $self = shift;
94 1         2 my $File = shift;
95              
96 1         1 my @CharTable;
97 1         5 my $CharTableSize = $self->{LastChar} - $self->{FirstChar} + 2;
98 1         3 my $BitsOffset = 148 + $CharTableSize * 6;
99 1         2 my $Offset = $BitsOffset;
100 1         2 my $Bits = '';
101 1         8 my $Sentinel =
102             {
103             Width => $self->{AvgWidth}
104             , BitMap => [ ('-' x $self->{AvgWidth} ) x $self->{PixHeight} ]
105             };
106 1         4 for ( @{$self->{Chars}}, $Sentinel )
  1         5  
107             {
108 257         230 my @Bmp = @{$_->{BitMap}};
  257         1144  
109 257         1798 tr/-#/01/ for @Bmp;
110 257         277 my $Bmp;
111 257         589 for ( my $Offset = 0; $Offset < $_->{Width}; $Offset += 8 )
112             {
113 257         2876 $Bmp .= pack 'B8', substr $_, $Offset for @Bmp;
114             }
115 257         334 push @CharTable, $_->{Width};
116 257         244 push @CharTable, $Offset;
117 257         241 $Offset += length $Bmp;
118 257         771 $Bits .= $Bmp;
119             }
120 1         26 $self->{BitsOffset} = $BitsOffset;
121 1         3 $self->{Face} = $Offset;
122 1         4 $self->{Size} = $Offset + length( $self->{FaceName} ) + 1;
123              
124 1         33 my $s = pack "@v", @$self{@k};
125 1         46 $s .= pack 'SL' x $CharTableSize, @CharTable;
126 1         15 $s .= $Bits;
127 1         4 $s .= pack 'Z*', $self->{FaceName};
128              
129 1 50       151 open my $f, ">$File" or die $!;
130 1         4 binmode $f;
131 1         111 print $f $s;
132             }
133             # -----------------------------------------------------------------------------
134             sub save_yaml
135             # -----------------------------------------------------------------------------
136             {
137 1     1 1 1081 my $self = shift;
138 1         1 my $File = shift;
139              
140 1         3 local $YAML::Indent = 1;
141              
142 1         18 YAML::DumpFile( $File, $self );
143             }
144             # -----------------------------------------------------------------------------
145             sub load_yaml
146             # -----------------------------------------------------------------------------
147             {
148 1     1 1 15 my $class = shift;
149 1         3 my $File = shift;
150              
151 1         9 my $self = YAML::LoadFile( $File );
152              
153 1         1607938 bless $self, $class;
154             }
155             # -----------------------------------------------------------------------------
156             sub save_pbm
157             # -----------------------------------------------------------------------------
158             {
159 1     1 1 357 my $self = shift;
160 1         2 my $File = shift;
161              
162 1 50       167 open my $f,'>', $File or die "Failed to open `$File': $!";
163              
164 1         3 my $Width = 0;
165 1         1 $Width += length $_->{BitMap}[0] for @{$self->{Chars}};
  1         244  
166 1         6 local $\ = "\n";
167 1         6 print $f 'P1';
168 1         7 print $f $Width;
169 1         3 print $f $self->{PixHeight};
170 1         4 local $\ = '';
171              
172 1         4 for my $y ( 0 .. $self->{PixHeight} - 1 )
173             {
174 13         13 for ( @{$self->{Chars}} )
  13         22  
175             {
176 3328         4273 my $s = $_->{BitMap}[$y];
177 3328         3032 $s =~ tr/-#/01/;;
178 3328         4508 print $f $s;
179             }
180 13         67 print $f "\n";
181             }
182             }
183             # -----------------------------------------------------------------------------
184             1;
185              
186             =head1 NAME
187              
188             Font::FNT - Load, manipulate and save Windows raster fonts
189              
190             =head1 SYNOPSIS
191              
192             use Font::FNT();
193              
194              
195             my $fnt = Font::FNT->load('test.fnt');
196              
197             $fnt->save_yaml('test.yml');
198              
199              
200             # scite test.yml
201              
202              
203             $fnt = Font::FNT->load_yaml('test.yml');
204              
205             $fnt->save_pbm('test.pbm');
206              
207              
208             $fnt->save('test.fnt');
209              
210             =head1 DESCRIPTION
211              
212             This module provides basic load, manipulate and save functionality for
213             Windows 3.00 raster fonts (.FNT files).
214              
215             =head2 Methods
216              
217             =over
218              
219             =item load( $filename )
220              
221             Loads a .FNT file. This is a constructor method and returns an
222             Font::FNT instance.
223              
224             =item save_yaml( $filename )
225              
226             Saves a Font::FNT instance into a notepadable format (YAML).
227             You can use your prefered text editor to manipulate that serialized
228             Font::FNT instance.
229              
230             =item load_yaml( $filename )
231              
232             Loads a YAML file (which should contain a serialized Font::FNT instance).
233             This is a constructor method and returns an Font::FNT instance.
234              
235             =item save_pbm( $filename )
236              
237             Saves a Font::FNT instance as portable bitmap (pbm) file.
238             Yo can use this for preview purposes.
239              
240             =item save( $filename )
241              
242             Saves a Font::FNT instance as .FNT file.
243              
244             =back
245              
246             =head1 EXAMPLE
247              
248             The test directory contains a serialized Font::FNT instance
249             (1252_13x8_OEM.yml) that can be used to install a nice Console
250             (Terminal) font - at least on a Windows NT 4.0 box.
251             Other Windows versions may require other steps.
252              
253             The font is similar to the bold BorlandTE font (BORTE.FON) or
254             Raize font.
255              
256             Let's create the .FNT file first:
257              
258             use Font::FNT();
259             Font::FNT->load_yaml('t/1252_13x8_OEM.yml')->save('t/1252_13x8_OEM.fnt');
260              
261             Normally, various .FNT files for different sizes and weights are
262             packaged together as resources in a Windows DLL (.FON file).
263             BTW: most .FON files are good old Windows 3.1 DLLs.
264             For our purpose, the .FNT file is o.k.
265              
266             Next, add something like
267              
268             [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts]
269             "1252 13x8 OEM"="1252_13x8_OEM.fnt"
270              
271             to your registry, copy the font by hand
272              
273             copy t\1252_13x8_OEM.fnt %SystemRoot%\Fonts
274              
275             and reboot your system.
276              
277             If you don't need control about your registry entries, then
278             you can simply drag'n drop t/1252_13x8_OEM.fnt into the Fonts
279             Control Panel Applet.
280              
281             Now, if you open the Console properties dialog or the Console
282             Control Panel Applet, go to the font tab and select 'Raster Fonts',
283             you should see a new entry in the 'Size' listbox:
284              
285             8 x 13
286              
287             Configuring the font in the Control Panel results in the following
288             registry entries:
289              
290             [HKEY_CURRENT_USER\Console]
291             "FaceName"="Terminal"
292             "FontSize"=dword:000d0008
293              
294             Finally, you should change the Console codepage:
295              
296             mode con cp select=1252
297              
298             or
299              
300             chcp 1252
301              
302             To change the codepage permanently, add the following to your
303             registry:
304              
305             [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage]
306             "OEMCP"="1252"
307              
308              
309             =head1 AUTHOR
310              
311             Steffen Goeldner
312              
313             =head1 COPYRIGHT
314              
315             Copyright (c) 2004 Steffen Goeldner. All rights reserved.
316              
317             This program is free software; you can redistribute it and/or
318             modify it under the same terms as Perl itself.
319              
320             =head1 SEE ALSO
321              
322             =head2 Microsoft Knowledge Base Article
323              
324             =over
325              
326             =item Windows Developers Notes: Font-File Format
327              
328             http://support.microsoft.com/?scid=kb;EN-US;65123
329              
330             =item MyFont.exe - Creating a Custom Raster Font
331              
332             http://support.microsoft.com/?scid=kb;EN-US;76535
333              
334             =item Necessary Criteria for Fonts to Be Available in a Command Window
335              
336             http://support.microsoft.com/?scid=kb;EN-US;247815
337              
338             =item How to Change the OEM Code Page of Windows NT and Windows 95
339              
340             http://support.microsoft.com/?scid=kb;EN-US;153449
341              
342             =back
343              
344             =head2 Microsoft Global Development and Computing Portal - Code Pages
345              
346             http://www.microsoft.com/globaldev/reference/cphome.mspx
347              
348             =head2 Adobe Glyph Bitmap Distribution Format (BDF) Specification
349              
350             http://partners.adobe.com/asn/developer/pdfs/tn/5005.BDF_Spec.pdf
351              
352             This format is similar in spirit to the YAML serialization of a
353             Font::FNT instance.
354              
355             =head2 Perl modules
356              
357             L, L, L.
358              
359             =cut