File Coverage

blib/lib/Image/ExifTool/Font.pm
Criterion Covered Total %
statement 127 308 41.2
branch 56 218 25.6
condition 39 143 27.2
subroutine 8 14 57.1
pod 0 10 0.0
total 230 693 33.1


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: Font.pm
3             #
4             # Description: Read meta information from font files
5             #
6             # Revisions: 2010/01/15 - P. Harvey Created
7             #
8             # References: 1) http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html
9             # 2) http://www.microsoft.com/typography/otspec/otff.htm
10             # 3) http://partners.adobe.com/public/developer/opentype/index_font_file.html
11             # 4) http://partners.adobe.com/public/developer/en/font/5178.PFM.pdf
12             # 5) http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/modules/compiler/src/java/flex2/compiler/util/MimeMappings.java
13             # 6) http://www.adobe.com/devnet/font/pdfs/5004.AFM_Spec.pdf
14             # 7) https://www.w3.org/TR/WOFF/
15             # 8) https://www.w3.org/TR/WOFF2/
16             #------------------------------------------------------------------------------
17              
18             package Image::ExifTool::Font;
19              
20 1     1   11734 use strict;
  1         3  
  1         54  
21 1     1   8 use vars qw($VERSION %ttLang);
  1         3  
  1         72  
22 1     1   7 use Image::ExifTool qw(:DataAccess :Utils);
  1         3  
  1         316  
23 1     1   1560 use Image::ExifTool::XMP;
  1         9  
  1         9136  
24              
25             $VERSION = '1.12';
26              
27             sub ProcessOTF($$);
28              
29             # OTF tags to process (skip all others)
30             my %processTag = ( name => 1, C2PA => 1 );
31              
32             # TrueType 'name' platform codes
33             my %ttPlatform = (
34             0 => 'Unicode',
35             1 => 'Macintosh',
36             2 => 'ISO',
37             3 => 'Windows',
38             4 => 'Custom',
39             );
40              
41             # convert TrueType 'name' character encoding to ExifTool Charset (ref 1/2)
42             my %ttCharset = (
43             Macintosh => {
44             0 => 'MacRoman', 17 => 'MacMalayalam',
45             1 => 'MacJapanese', 18 => 'MacSinhalese',
46             2 => 'MacChineseTW', 19 => 'MacBurmese',
47             3 => 'MacKorean', 20 => 'MacKhmer',
48             4 => 'MacArabic', 21 => 'MacThai',
49             5 => 'MacHebrew', 22 => 'MacLaotian',
50             6 => 'MacGreek', 23 => 'MacGeorgian',
51             7 => 'MacCyrillic', 24 => 'MacArmenian', # 7=Russian
52             8 => 'MacRSymbol', 25 => 'MacChineseCN',
53             9 => 'MacDevanagari', 26 => 'MacTibetan',
54             10 => 'MacGurmukhi', 27 => 'MacMongolian',
55             11 => 'MacGujarati', 28 => 'MacGeez',
56             12 => 'MacOriya', 29 => 'MacCyrillic', # 29=Slavic
57             13 => 'MacBengali', 30 => 'MacVietnam',
58             14 => 'MacTamil', 31 => 'MacSindhi',
59             15 => 'MacTelugu', 32 => '', # 32=uninterpreted
60             16 => 'MacKannada',
61             },
62             Windows => {
63             0 => 'Symbol', 4 => 'Big5',
64             1 => 'UCS2', 5 => 'Wansung',
65             2 => 'ShiftJIS', 6 => 'Johab',
66             3 => 'PRC', 10 => 'UCS4',
67             },
68             Unicode => {
69             # (we don't currently handle the various Unicode flavours)
70             0 => 'UCS2', # Unicode 1.0 semantics
71             1 => 'UCS2', # Unicode 1.1 semantics
72             2 => 'UCS2', # ISO 10646 semantics
73             3 => 'UCS2', # Unicode 2.0 and onwards semantics, Unicode BMP only.
74             4 => 'UCS2', # Unicode 2.0 and onwards semantics, Unicode full repertoire.
75             # 5 => Unicode Variation Sequences (not used in Naming table)
76             },
77             ISO => { # (deprecated)
78             0 => 'UTF8', # (7-bit ASCII)
79             1 => 'UCS2', # ISO 10646
80             2 => 'Latin', # ISO 8859-1
81             },
82             Custom => { },
83             );
84              
85             # convert TrueType 'name' language code to ExifTool language code
86             %ttLang = (
87             # Macintosh language codes (also used by QuickTime.pm)
88             # oddities:
89             # 49 - Cyrillic version 83 - Roman
90             # 50 - Arabic version 84 - Arabic
91             # 146 - with dot above
92             Macintosh => {
93             0 => 'en', 24 => 'lt', 48 => 'kk', 72 => 'ml', 129 => 'eu',
94             1 => 'fr', 25 => 'pl', 49 => 'az', 73 => 'kn', 130 => 'ca',
95             2 => 'de', 26 => 'hu', 50 => 'az', 74 => 'ta', 131 => 'la',
96             3 => 'it', 27 => 'et', 51 => 'hy', 75 => 'te', 132 => 'qu',
97             4 => 'nl-NL', 28 => 'lv', 52 => 'ka', 76 => 'si', 133 => 'gn',
98             5 => 'sv', 29 => 'smi', 53 => 'ro', 77 => 'my', 134 => 'ay',
99             6 => 'es', 30 => 'fo', 54 => 'ky', 78 => 'km', 135 => 'tt',
100             7 => 'da', 31 => 'fa', 55 => 'tg', 79 => 'lo', 136 => 'ug',
101             8 => 'pt', 32 => 'ru', 56 => 'tk', 80 => 'vi', 137 => 'dz',
102             9 => 'no', 33 => 'zh-CN', 57 => 'mn-MN', 81 => 'id', 138 => 'jv',
103             10 => 'he', 34 => 'nl-BE', 58 => 'mn-CN', 82 => 'tl', 139 => 'su',
104             11 => 'ja', 35 => 'ga', 59 => 'ps', 83 => 'ms-MY', 140 => 'gl',
105             12 => 'ar', 36 => 'sq', 60 => 'ku', 84 => 'ms-BN', 141 => 'af',
106             13 => 'fi', 37 => 'ro', 61 => 'ks', 85 => 'am', 142 => 'br',
107             14 => 'el', 38 => 'cs', 62 => 'sd', 86 => 'ti', 144 => 'gd',
108             15 => 'is', 39 => 'sk', 63 => 'bo', 87 => 'om', 145 => 'gv',
109             16 => 'mt', 40 => 'sl', 64 => 'ne', 88 => 'so', 146 => 'ga',
110             17 => 'tr', 41 => 'yi', 65 => 'sa', 89 => 'sw', 147 => 'to',
111             18 => 'hr', 42 => 'sr', 66 => 'mr', 90 => 'rw', 148 => 'el',
112             19 => 'zh-TW', 43 => 'mk', 67 => 'bn', 91 => 'rn', 149 => 'kl',
113             20 => 'ur', 44 => 'bg', 68 => 'as', 92 => 'ny', 150 => 'az',
114             21 => 'hi', 45 => 'uk', 69 => 'gu', 93 => 'mg',
115             22 => 'th', 46 => 'be', 70 => 'pa', 94 => 'eo',
116             23 => 'ko', 47 => 'uz', 71 => 'or', 128 => 'cy',
117             },
118             # Windows language codes (http://msdn.microsoft.com/en-us/library/0h88fahh(VS.85).aspx)
119             # Notes: This isn't an exact science. The reference above gives language codes
120             # which are different from some ISO 639-1 numbers. Also, some Windows language
121             # codes don't appear to have ISO 639-1 equivalents.
122             # 0x0428 - fa by ref above
123             # 0x048c - no ISO equivalent
124             # 0x081a/0x83c - sr-SP
125             # 0x0c0a - modern?
126             # 0x2409 - Caribbean country code not found in ISO 3166-1
127             Windows => {
128             0x0401 => 'ar-SA', 0x0438 => 'fo', 0x0481 => 'mi', 0x1409 => 'en-NZ',
129             0x0402 => 'bg', 0x0439 => 'hi', 0x0482 => 'oc', 0x140a => 'es-CR',
130             0x0403 => 'ca', 0x043a => 'mt', 0x0483 => 'co', 0x140c => 'fr-LU',
131             0x0404 => 'zh-TW', 0x043b => 'se-NO', 0x0484 => 'gsw', 0x141a => 'bs-BA',
132             0x0405 => 'cs', 0x043c => 'gd', 0x0485 => 'sah', 0x143b => 'smj-SE',
133             0x0406 => 'da', 0x043d => 'yi', 0x0486 => 'ny', 0x1801 => 'ar-MA',
134             0x0407 => 'de-DE', 0x043e => 'ms-MY', 0x0487 => 'rw', 0x1809 => 'en-IE',
135             0x0408 => 'el', 0x043f => 'kk', 0x048c => 'Dari', 0x180a => 'es-PA',
136             0x0409 => 'en-US', 0x0440 => 'ky', 0x0801 => 'ar-IQ', 0x180c => 'fr-MC',
137             0x040a => 'es-ES', 0x0441 => 'sw', 0x0804 => 'zh-CN', 0x181a => 'sr-BA',
138             0x040b => 'fi', 0x0442 => 'tk', 0x0807 => 'de-CH', 0x183b => 'sma-NO',
139             0x040c => 'fr-FR', 0x0443 => 'uz-UZ', 0x0809 => 'en-GB', 0x1c01 => 'ar-TN',
140             0x040d => 'he', 0x0444 => 'tt', 0x080a => 'es-MX', 0x1c09 => 'en-ZA',
141             0x040e => 'hu', 0x0445 => 'bn-IN', 0x080c => 'fr-BE', 0x1c0a => 'es-DO',
142             0x040f => 'is', 0x0446 => 'pa', 0x0810 => 'it-CH', 0x1c1a => 'sr-BA',
143             0x0410 => 'it-IT', 0x0447 => 'gu', 0x0813 => 'nl-BE', 0x1c3b => 'sma-SE',
144             0x0411 => 'ja', 0x0448 => 'wo', 0x0814 => 'nn', 0x2001 => 'ar-OM',
145             0x0412 => 'ko', 0x0449 => 'ta', 0x0816 => 'pt-PT', 0x2009 => 'en-JM',
146             0x0413 => 'nl-NL', 0x044a => 'te', 0x0818 => 'ro-MO', 0x200a => 'es-VE',
147             0x0414 => 'no-NO', 0x044b => 'kn', 0x0819 => 'ru-MO', 0x201a => 'bs-BA',
148             0x0415 => 'pl', 0x044c => 'ml', 0x081a => 'sr-RS', 0x203b => 'sms',
149             0x0416 => 'pt-BR', 0x044d => 'as', 0x081d => 'sv-FI', 0x2401 => 'ar-YE',
150             0x0417 => 'rm', 0x044e => 'mr', 0x082c => 'az-AZ', 0x2409 => 'en-CB',
151             0x0418 => 'ro', 0x044f => 'sa', 0x082e => 'dsb', 0x240a => 'es-CO',
152             0x0419 => 'ru', 0x0450 => 'mn-MN', 0x083b => 'se-SE', 0x243b => 'smn',
153             0x041a => 'hr', 0x0451 => 'bo', 0x083c => 'ga', 0x2801 => 'ar-SY',
154             0x041b => 'sk', 0x0452 => 'cy', 0x083e => 'ms-BN', 0x2809 => 'en-BZ',
155             0x041c => 'sq', 0x0453 => 'km', 0x0843 => 'uz-UZ', 0x280a => 'es-PE',
156             0x041d => 'sv-SE', 0x0454 => 'lo', 0x0845 => 'bn-BD', 0x2c01 => 'ar-JO',
157             0x041e => 'th', 0x0456 => 'gl', 0x0850 => 'mn-CN', 0x2c09 => 'en-TT',
158             0x041f => 'tr', 0x0457 => 'kok', 0x085d => 'iu-CA', 0x2c0a => 'es-AR',
159             0x0420 => 'ur', 0x045a => 'syr', 0x085f => 'tmh', 0x3001 => 'ar-LB',
160             0x0421 => 'id', 0x045b => 'si', 0x086b => 'qu-EC', 0x3009 => 'en-ZW',
161             0x0422 => 'uk', 0x045d => 'iu-CA', 0x0c01 => 'ar-EG', 0x300a => 'es-EC',
162             0x0423 => 'be', 0x045e => 'am', 0x0c04 => 'zh-HK', 0x3401 => 'ar-KW',
163             0x0424 => 'sl', 0x0461 => 'ne', 0x0c07 => 'de-AT', 0x3409 => 'en-PH',
164             0x0425 => 'et', 0x0462 => 'fy', 0x0c09 => 'en-AU', 0x340a => 'es-CL',
165             0x0426 => 'lv', 0x0463 => 'ps', 0x0c0a => 'es-ES', 0x3801 => 'ar-AE',
166             0x0427 => 'lt', 0x0464 => 'fil', 0x0c0c => 'fr-CA', 0x380a => 'es-UY',
167             0x0428 => 'tg', 0x0465 => 'dv', 0x0c1a => 'sr-RS', 0x3c01 => 'ar-BH',
168             0x042a => 'vi', 0x0468 => 'ha', 0x0c3b => 'se-FI', 0x3c0a => 'es-PY',
169             0x042b => 'hy', 0x046a => 'yo', 0x0c6b => 'qu-PE', 0x4001 => 'ar-QA',
170             0x042c => 'az-AZ', 0x046b => 'qu-BO', 0x1001 => 'ar-LY', 0x4009 => 'en-IN',
171             0x042d => 'eu', 0x046c => 'st', 0x1004 => 'zh-SG', 0x400a => 'es-BO',
172             0x042e => 'hsb', 0x046d => 'ba', 0x1007 => 'de-LU', 0x4409 => 'en-MY',
173             0x042f => 'mk', 0x046e => 'lb', 0x1009 => 'en-CA', 0x440a => 'es-SV',
174             0x0430 => 'st', 0x046f => 'kl', 0x100a => 'es-GT', 0x4809 => 'en-SG',
175             0x0431 => 'ts', 0x0470 => 'ig', 0x100c => 'fr-CH', 0x480a => 'es-HN',
176             0x0432 => 'tn', 0x0478 => 'yi', 0x101a => 'hr-BA', 0x4c0a => 'es-NI',
177             0x0434 => 'xh', 0x047a => 'arn', 0x103b => 'smj-NO',0x500a => 'es-PR',
178             0x0435 => 'zu', 0x047c => 'moh', 0x1401 => 'ar-DZ', 0x540a => 'es-US',
179             0x0436 => 'af', 0x047e => 'br', 0x1404 => 'zh-MO',
180             0x0437 => 'ka', 0x0480 => 'ug', 0x1407 => 'de-LI',
181             },
182             Unicode => { },
183             ISO => { },
184             Custom => { },
185             );
186              
187             # the 63 known WOFF2 tags
188             my @knownTags = (
189             'cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2', 'post', 'cvt',
190             'fpgm', 'glyf', 'loca', 'prep', 'CFF', 'VORG', 'EBDT', 'EBLC', 'gasp',
191             'hdmx', 'kern', 'LTSH', 'PCLT', 'VDMX', 'vhea', 'vmtx', 'BASE', 'GDEF',
192             'GPOS', 'GSUB', 'EBSC', 'JSTF', 'MATH', 'CBDT', 'CBLC', 'COLR', 'CPAL',
193             'SVG', 'sbix', 'acnt', 'avar', 'bdat', 'bloc', 'bsln', 'cvar', 'fdsc',
194             'feat', 'fmtx', 'fvar', 'gvar', 'hsty', 'just', 'lcar', 'mort', 'morx',
195             'opbd', 'prop', 'trak', 'Zapf', 'Silf', 'Glat', 'Gloc', 'Feat', 'Sill',
196             );
197              
198             # eclectic table of tags for various format font files
199             %Image::ExifTool::Font::Main = (
200             GROUPS => { 2 => 'Document' },
201             NOTES => q{
202             This table contains a collection of tags found in font files of various
203             formats. ExifTool current recognizes OTF, TTF, TTC, DFONT, PFA, PFB, PFM,
204             AFM, ACFM, AMFM, WOFF and WOFF2 font files.
205             },
206             name => {
207             SubDirectory => { TagTable => 'Image::ExifTool::Font::Name' },
208             },
209             C2PA => {
210             SubDirectory => { TagTable => 'Image::ExifTool::Jpeg2000::Main', Start => 20 },
211             },
212             PFM => {
213             Name => 'PFMHeader',
214             SubDirectory => { TagTable => 'Image::ExifTool::Font::PFM' },
215             },
216             PSInfo => {
217             Name => 'PSFontInfo',
218             SubDirectory => { TagTable => 'Image::ExifTool::Font::PSInfo' },
219             },
220             AFM => {
221             Name => 'AFM',
222             SubDirectory => { TagTable => 'Image::ExifTool::Font::AFM' },
223             },
224             numfonts => 'NumFonts',
225             fontname => 'FontName',
226             postfont => {
227             Name => 'PostScriptFontName',
228             Description => 'PostScript Font Name',
229             },
230             # for WOFF files
231             WOFFVersion => { },
232             XML => {
233             SubDirectory => {
234             TagTable => 'Image::ExifTool::Font::XML',
235             IgnoreProp => { metadata => 1 },
236             },
237             },
238             );
239              
240             # TrueType name tags (ref 1/2)
241             %Image::ExifTool::Font::Name = (
242             GROUPS => { 2 => 'Document' },
243             NOTES => q{
244             The following tags are extracted from the TrueType font "name" table found
245             in OTF, TTF, TTC, DFONT, WOFF and WOFF2 files. These tags support localized
246             languages by adding a hyphen followed by a language code to the end of the
247             tag name (eg. "Copyright-fr" or "License-en-US"). Tags with no language
248             code use the default language of "en".
249             },
250             0 => { Name => 'Copyright', Groups => { 2 => 'Author' } },
251             1 => 'FontFamily',
252             2 => 'FontSubfamily',
253             3 => 'FontSubfamilyID',
254             4 => 'FontName', # full name
255             5 => 'NameTableVersion',
256             6 => { Name => 'PostScriptFontName', Description => 'PostScript Font Name' },
257             7 => 'Trademark',
258             8 => 'Manufacturer',
259             9 => 'Designer',
260             10 => 'Description',
261             11 => 'VendorURL',
262             12 => 'DesignerURL',
263             13 => 'License',
264             14 => 'LicenseInfoURL',
265             16 => 'PreferredFamily',
266             17 => 'PreferredSubfamily',
267             18 => 'CompatibleFontName',
268             19 => 'SampleText',
269             20 => {
270             Name => 'PostScriptFontName',
271             Description => 'PostScript Font Name',
272             },
273             21 => 'WWSFamilyName',
274             22 => 'WWSSubfamilyName',
275             );
276              
277             # PostScript Font Metric file header (ref 4)
278             %Image::ExifTool::Font::PFM = (
279             GROUPS => { 2 => 'Document' },
280             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
281             NOTES => 'Tags extracted from the PFM file header.',
282             0 => {
283             Name => 'PFMVersion',
284             Format => 'int16u',
285             PrintConv => 'sprintf("%x.%.2x",$val>>8,$val&0xff)',
286             },
287             6 => { Name => 'Copyright', Format => 'string[60]', Groups => { 2 => 'Author' } },
288             66 => { Name => 'FontType', Format => 'int16u' },
289             68 => { Name => 'PointSize', Format => 'int16u' },
290             70 => { Name => 'YResolution', Format => 'int16u' },
291             72 => { Name => 'XResolution', Format => 'int16u' },
292             74 => { Name => 'Ascent', Format => 'int16u' },
293             76 => { Name => 'InternalLeading', Format => 'int16u' },
294             78 => { Name => 'ExternalLeading', Format => 'int16u' },
295             80 => { Name => 'Italic' },
296             81 => { Name => 'Underline' },
297             82 => { Name => 'Strikeout' },
298             83 => { Name => 'Weight', Format => 'int16u' },
299             85 => { Name => 'CharacterSet' },
300             86 => { Name => 'PixWidth', Format => 'int16u' },
301             88 => { Name => 'PixHeight', Format => 'int16u' },
302             90 => { Name => 'PitchAndFamily' },
303             91 => { Name => 'AvgWidth', Format => 'int16u' },
304             93 => { Name => 'MaxWidth', Format => 'int16u' },
305             95 => { Name => 'FirstChar' },
306             96 => { Name => 'LastChar' },
307             97 => { Name => 'DefaultChar' },
308             98 => { Name => 'BreakChar' },
309             99 => { Name => 'WidthBytes', Format => 'int16u' },
310             # 101 => { Name => 'DeviceTypeOffset', Format => 'int32u' },
311             # 105 => { Name => 'FontNameOffset', Format => 'int32u' },
312             # 109 => { Name => 'BitsPointer', Format => 'int32u' },
313             # 113 => { Name => 'BitsOffset', Format => 'int32u' },
314             );
315              
316             # PostScript FontInfo attributes (PFA, PFB) (ref PH)
317             %Image::ExifTool::Font::PSInfo = (
318             GROUPS => { 2 => 'Document' },
319             NOTES => 'Tags extracted from PostScript font files (PFA and PFB).',
320             FullName => { },
321             FamilyName => { Name => 'FontFamily' },
322             Weight => { },
323             ItalicAngle => { },
324             isFixedPitch=> { },
325             UnderlinePosition => { },
326             UnderlineThickness => { },
327             Copyright => { Groups => { 2 => 'Author' } },
328             Notice => { Groups => { 2 => 'Author' } },
329             version => { },
330             FontName => { },
331             FontType => { },
332             FSType => { },
333             );
334              
335             # Adobe Font Metrics tags (AFM) (ref 6)
336             %Image::ExifTool::Font::AFM = (
337             GROUPS => { 2 => 'Document' },
338             NOTES => 'Tags extracted from Adobe Font Metrics files (AFM, ACFM and AMFM).',
339             'Creation Date' => { Name => 'CreateDate', Groups => { 2 => 'Time' } },
340             FontName => { },
341             FullName => { },
342             FamilyName => { Name => 'FontFamily' },
343             Weight => { },
344             Version => { },
345             Notice => { Groups => { 2 => 'Author' } },
346             EncodingScheme => { },
347             MappingScheme => { },
348             EscChar => { },
349             CharacterSet=> { },
350             Characters => { },
351             IsBaseFont => { },
352             # VVector => { },
353             IsFixedV => { },
354             CapHeight => { },
355             XHeight => { },
356             Ascender => { },
357             Descender => { },
358             );
359              
360             # WOFF XML
361             %Image::ExifTool::Font::XML = (
362             GROUPS => { 1 => 'XML', 2 => 'Document' },
363             PROCESS_PROC => \&Image::ExifTool::XMP::ProcessXMP,
364             NOTES => 'Tags found in WOFF and WOFF2 XML metadata.',
365             version => { },
366             uniqueidId => { Name => 'UniqueID' },
367             vendorName => { },
368             vendorUrl => { Name => 'VendorURL' },
369             vendorDir => { },
370             vendorClass => { },
371             creditsCreditName => { Name => 'CreditName' },
372             creditsCreditUrl => { Name => 'CreditURL' },
373             creditsCreditRole => { Name => 'CreditRole' },
374             creditsCreditDir => { Name => 'CreditDir' },
375             creditsCreditClass => { Name => 'CreditClass' },
376             descriptionUrl => { },
377             descriptionText => { Name => 'Description' },
378             licenseUrl => { Name => 'LicenseURL' },
379             licenseId => { Name => 'LicenseID' },
380             licenseText => { Name => 'License' },
381             copyrightText => { Name => 'Copyright', Groups => { 2 => 'Author' } },
382             trademarkText => { Name => 'Trademark' },
383             licenseeDir => { },
384             licenseeName => { },
385             licenseeClass => { },
386             extensionId => { Name => 'ExtensionID' },
387             extensionName => { },
388             extensionItemId => { Name => 'ExtensionItemID' },
389             extensionItemName => { },
390             extensionItemValue => { },
391             );
392              
393             #------------------------------------------------------------------------------
394             # Read information from a TrueType font collection (TTC) (refs 2,3)
395             # Inputs: 0) ExifTool ref, 1) dirInfo ref
396             # Returns: 1 on success, 0 if this wasn't a valid TrueType font collection
397             sub ProcessTTC($$)
398             {
399 0     0 0 0 my ($et, $dirInfo) = @_;
400 0         0 my $raf = $$dirInfo{RAF};
401 0         0 my ($buff, $i);
402              
403 0 0       0 return 0 unless $raf->Read($buff, 12) == 12;
404 0 0       0 return 0 unless $buff =~ /^ttcf\0[\x01\x02]\0\0/;
405 0         0 SetByteOrder('MM');
406 0         0 my $num = Get32u(\$buff, 8);
407             # might as well put a limit on the number of fonts we will parse (< 256)
408 0 0 0     0 return 0 unless $num < 0x100 and $raf->Read($buff, $num * 4) == $num * 4;
409 0         0 $et->SetFileType('TTC');
410 0 0 0     0 return 1 if $$et{OPTIONS}{FastScan} and $$et{OPTIONS}{FastScan} == 3;
411 0         0 my $tagTablePtr = GetTagTable('Image::ExifTool::Font::Main');
412 0         0 $et->HandleTag($tagTablePtr, 'numfonts', $num);
413             # loop through all fonts in the collection
414 0         0 for ($i=0; $i<$num; ++$i) {
415 0         0 my $n = $i + 1;
416 0         0 $et->VPrint(0, "Font $n:\n");
417 0         0 $$et{SET_GROUP1} = "+$n";
418 0         0 my $offset = Get32u(\$buff, $i * 4);
419 0 0       0 $raf->Seek($offset, 0) or last;
420 0 0       0 ProcessOTF($et, $dirInfo) or last;
421             }
422 0         0 delete $$et{SET_GROUP1};
423 0         0 return 1;
424             }
425              
426             #------------------------------------------------------------------------------
427             # Process an OTF tag table entry (refs 1,2)
428             # Inputs: 0) ExifTool ref, 1) entry index, 2) tag, 3) data ref,
429             # 4) offset if uncompressed, 5) true to skip because data is transformed
430             # Returns: undef on success, 0 to stop processing table, or error string otherwise
431             sub ProcessTableEntry($$$$;$$)
432             {
433 2     2 0 10 my ($et, $idx, $tag, $dataPt, $offset, $transformed) = @_;
434 2         7 my $verbose = $et->Options('Verbose');
435 2         6 my $size = length $$dataPt;
436              
437 2 50       7 $offset or $offset = 0;
438 2 50       7 if ($verbose) {
439 0         0 $tag =~ s/([\0-\x1f\x7f-\xff])/sprintf('\x%.2x',ord $1)/ge;
  0         0  
440             my $str = sprintf("%s%d) Tag '%s' (offset 0x%.4x, %d bytes)\n",
441 0         0 $$et{INDENT}, $idx, $tag, $offset, $size);
442 0         0 $et->VPrint(0, $str);
443 0 0       0 $et->VerboseDump($dataPt, Addr => $offset) if $verbose > 2;
444 0 0       0 return undef unless $processTag{$tag};
445             }
446 2 50 33     14 return undef if $transformed or $size < 8;
447 2 50       7 unless ($tag eq 'name') {
448 0         0 my $tagTablePtr = GetTagTable('Image::ExifTool::Font::Main');
449 0         0 $et->HandleTag($tagTablePtr, $tag, undef, DataPt => $dataPt, Size => length($$dataPt));
450 0         0 return undef;
451             }
452             # process the 'name' tag
453 2         7 my $entries = Get16u($dataPt, 2);
454 2         5 my $recEnd = 6 + $entries * 12;
455 2 50       6 if ($recEnd > $size) {
456 0         0 $et->Warn('Truncated name record');
457 0         0 return 0;
458             }
459 2         5 my $strStart = Get16u($dataPt, 4);
460 2 50 33     10 if ($strStart < $recEnd or $strStart > $size) {
461 0         0 $et->Warn('Invalid string offset');
462 0         0 return 0;
463             }
464             # parse language-tag record (in format 1 Naming table only) (ref 2)
465 2         5 my ($i, %langTag);
466 2 50 33     8 if (Get16u($dataPt, 0) == 1 and $recEnd + 2 <= $size) {
467 0         0 my $langTags = Get16u($dataPt, $recEnd);
468 0 0 0     0 if ($langTags and $recEnd + 2 + $langTags * 4 < $size) {
469 0         0 for ($i=0; $i<$langTags; ++$i) {
470 0         0 my $pt = $recEnd + 2 + $i * 4;
471 0         0 my $langLen = Get16u($dataPt, $pt);
472             # make sure the language string length is reasonable (UTF-16BE)
473 0 0 0     0 last if $langLen == 0 or $langLen & 0x01 or $langLen > 40;
      0        
474 0         0 my $langPt = Get16u($dataPt, $pt + 2) + $strStart;
475 0 0       0 last if $langPt + $langLen > $size;
476 0         0 my $lang = substr($$dataPt, $langPt, $langLen);
477 0         0 $lang = $et->Decode($lang,'UCS2','MM','UTF8');
478 0         0 $lang =~ tr/-_a-zA-Z0-9//dc; # remove naughty characters
479 0         0 $langTag{$i + 0x8000} = $lang;
480             }
481             }
482             }
483 2         13 my $tagTablePtr = GetTagTable('Image::ExifTool::Font::Name');
484 2         7 my $oldIndent = $$et{INDENT};
485 2         8 $$et{INDENT} .= '| ';
486 2 50       9 $et->VerboseDir('Name', $entries) if $verbose;
487 2         9 for ($i=0; $i<$entries; ++$i) {
488 63         115 my $pt = 6 + $i * 12;
489 63         152 my $platform = Get16u($dataPt, $pt);
490 63         143 my $encoding = Get16u($dataPt, $pt + 2);
491 63         133 my $langID = Get16u($dataPt, $pt + 4);
492 63         127 my $nameID = Get16u($dataPt, $pt + 6);
493 63         149 my $strLen = Get16u($dataPt, $pt + 8);
494 63         118 my $strPt = Get16u($dataPt, $pt + 10) + $strStart;
495 63 50       134 if ($strPt + $strLen <= $size) {
496 63         215 my $val = substr($$dataPt, $strPt, $strLen);
497 63         95 my ($lang, $charset, $extra);
498 63         149 my $sys = $ttPlatform{$platform};
499             # translate from specified encoding
500 63 50       137 if ($sys) {
501 63   33     205 $lang = $ttLang{$sys}{$langID} || $langTag{$langID};
502 63         131 $charset = $ttCharset{$sys}{$encoding};
503 63 50       145 if (not $charset) {
504 0 0 0     0 if (not defined $charset and not $$et{FontWarn}) {
505 0         0 $et->Warn("Unknown $sys character set ($encoding)");
506 0         0 $$et{FontWarn} = 1;
507             }
508             } else {
509             # translate to ExifTool character set
510 63         198 $val = $et->Decode($val, $charset);
511             }
512             } else {
513 0         0 $et->Warn("Unknown platform ($platform) for name $nameID");
514             }
515             # get the tagInfo for our specific language (use 'en' for default)
516 63         186 my $tagInfo = $et->GetTagInfo($tagTablePtr, $nameID);
517 63 100 66     289 if ($tagInfo and $lang and $lang ne 'en') {
      100        
518 30         121 my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $lang);
519 30 50       104 $tagInfo = $langInfo if $langInfo;
520             }
521 63 50       131 if ($verbose) {
522 0 0       0 $langID > 0x400 and $langID = sprintf('0x%x', $langID);
523 0   0     0 $extra = ", Plat=$platform/" . ($sys || 'Unknown') . ', ' .
      0        
      0        
524             "Enc=$encoding/" . ($charset || 'Unknown') . ', ' .
525             "Lang=$langID/" . ($lang || 'Unknown');
526             }
527 63         231 $et->HandleTag($tagTablePtr, $nameID, $val,
528             TagInfo => $tagInfo,
529             DataPt => $dataPt,
530             DataPos => $offset,
531             Start => $strPt,
532             Size => $strLen,
533             Index => $i,
534             Extra => $extra,
535             );
536             }
537             }
538 2         9 $$et{INDENT} = $oldIndent;
539 2 50       11 return $verbose ? undef : 0;
540             }
541              
542             #------------------------------------------------------------------------------
543             # Read information from a TrueType font file (OTF or TTF) (refs 1,2)
544             # Inputs: 0) ExifTool ref, 1) dirInfo ref
545             # Returns: 1 on success, 0 if this wasn't a valid TrueType font file
546             sub ProcessOTF($$)
547             {
548 2     2 0 6 my ($et, $dirInfo) = @_;
549 2         5 my $raf = $$dirInfo{RAF};
550 2         49 my ($tbl, $buff, $pos, $i);
551 2   100     14 my $base = $$dirInfo{Base} || 0;
552              
553 2 50       8 return 0 unless $raf->Read($buff, 12) == 12;
554 2 50       16 return 0 unless $buff =~ /^(\0\x01\0\0|OTTO|true|typ1|\xa5(kbd|lst))[\0\x01]/;
555              
556 2 50       17 $et->SetFileType($1 eq 'OTTO' ? 'OTF' : 'TTF');
557 2 50 33     11 return 1 if $$et{OPTIONS}{FastScan} and $$et{OPTIONS}{FastScan} == 3;
558 2         10 SetByteOrder('MM');
559 2         9 my $numTables = Get16u(\$buff, 4);
560 2 50 33     13 return 0 unless $numTables > 0 and $numTables < 0x200;
561 2         5 my $len = $numTables * 16;
562 2 50       9 return 0 unless $raf->Read($tbl, $len) == $len;
563              
564 2         8 my $verbose = $et->Options('Verbose');
565              
566 2         9 for ($pos=0, $i=0; $pos<$len; $pos+=16, ++$i) {
567             # look for tags to process
568 16         21 my $tag = substr($tbl, $pos, 4);
569 16 100 66     46 next unless $processTag{$tag} or $verbose;
570 2         7 my $offset = Get32u(\$tbl, $pos + 8);
571 2         7 my $size = Get32u(\$tbl, $pos + 12);
572 2 50 33     7 unless ($raf->Seek($offset+$base, 0) and $raf->Read($buff, $size) == $size) {
573 0         0 $et->Warn("Error reading '${tag}' data");
574 0         0 next;
575             }
576 2         11 my $err = ProcessTableEntry($et, $i, $tag, \$buff, $offset);
577 2 50       10 $err and $et->Warn($err), last;
578 2 50       9 last if defined $err;
579             }
580 2         13 return 1;
581             }
582              
583             #------------------------------------------------------------------------------
584             # Read information from an Adobe Font Metrics file (AFM, ACFM, AMFM) (ref 6)
585             # Inputs: 0) ExifTool ref, 1) dirInfo ref
586             # Returns: 1 on success, 0 if this wasn't a recognized AFM-type file
587             sub ProcessAFM($$)
588             {
589 1     1 0 2 my ($et, $dirInfo) = @_;
590 1         2 my $raf = $$dirInfo{RAF};
591 1         2 my ($buff, $comment);
592              
593 1         789 require Image::ExifTool::PostScript;
594 1         6 local $/ = Image::ExifTool::PostScript::GetInputRecordSeparator($raf);
595 1         5 $raf->ReadLine($buff);
596 1 50       11 return 0 unless $buff =~ /^Start(Comp|Master)?FontMetrics\s+\d+/;
597 1 0       4 my $ftyp = $1 ? ($1 eq 'Comp' ? 'ACFM' : 'AMFM') : 'AFM';
    50          
598 1         6 $et->SetFileType($ftyp, 'application/x-font-afm');
599 1 50 33     5 return 1 if $$et{OPTIONS}{FastScan} and $$et{OPTIONS}{FastScan} == 3;
600 1         3 my $tagTablePtr = GetTagTable('Image::ExifTool::Font::AFM');
601              
602 1         3 for (;;) {
603 19 50       35 $raf->ReadLine($buff) or last;
604 19 100 100     34 if (defined $comment and $buff !~ /^Comment\s/) {
605 1         3 $et->FoundTag('Comment', $comment);
606 1         1 undef $comment;
607             }
608 19 50       64 $buff =~ /^(\w+)\s+(.*?)[\x0d\x0a]/ or next;
609 19         35 my ($tag, $val) = ($1, $2);
610 19 100 100     51 if ($tag eq 'Comment' and $val =~ /^(Creation Date):\s+(.*)/) {
611 1         3 ($tag, $val) = ($1, $2);
612             }
613 19         28 $val =~ s/^\((.*)\)$/$1/; # (some values may be in brackets)
614 19 100       26 if ($tag eq 'Comment') {
615             # concatinate all comments into a single value
616 1 50       3 $comment = defined($comment) ? "$comment\n$val" : $val;
617 1         3 next;
618             }
619 18 100       28 unless ($et->HandleTag($tagTablePtr, $tag, $val)) {
620             # end parsing if we start any subsection
621 6 100 66     19 last if $tag =~ /^Start/ and $tag ne 'StartDirection';
622             }
623             }
624 1         9 return 1;
625             }
626              
627             #------------------------------------------------------------------------------
628             # Read WOFF2 255UInt16 integer (ref 8)
629             # Inputs: 0) raf ref
630             # Returns: value, or undef on error
631             sub Read255UInt16($)
632             {
633 0     0 0 0 my $raf = shift;
634 0         0 my $buff;
635 0 0       0 return undef unless $raf->Read($buff, 1);
636 0         0 my $val = unpack('C', $buff);
637 0 0       0 if ($val == 253) {
    0          
    0          
638 0 0       0 return undef unless $raf->Read($buff, 2) == 2;
639 0         0 $val = unpack('n', $buff);
640             } elsif ($val == 254) {
641 0 0       0 return undef unless $raf->Read($buff, 1);
642 0         0 $val = unpack('C', $buff) + 253 * 2;
643             } elsif ($val == 255) {
644 0 0       0 return undef unless $raf->Read($buff, 1);
645 0         0 $val = unpack('C', $buff) + 253;
646             }
647 0         0 return $val;
648             }
649              
650             #------------------------------------------------------------------------------
651             # Read WOFF2 UIntBase128 integer (ref 8)
652             # Inputs: 0) raf ref
653             # Returns: value, or undef on error
654             sub ReadUIntBase128($)
655             {
656 0     0 0 0 my $raf = shift;
657 0         0 my $buff;
658 0         0 my $val = 0;
659 0         0 foreach (0..4) {
660 0 0       0 return undef unless $raf->Read($buff, 1);
661 0         0 my $byte = unpack('C', $buff);
662 0 0 0     0 return undef if not $_ and $byte == 0x80;
663 0 0       0 return undef if $val & 0xfe000000;
664 0         0 $val = ($val << 7) | ($byte & 0x7f);
665 0 0       0 return $val unless $byte & 0x80;
666             }
667 0         0 return undef; # can't be longer than 5 bytes
668             }
669              
670             #------------------------------------------------------------------------------
671             # Uncompress data
672             # Inputs: 0) ExifTool ref, 1) data ref
673             # Returns: true on success
674             sub Uncompress($$)
675             {
676 0     0 0 0 my ($et, $dataPt) = @_;
677 0         0 my $stat;
678 0 0       0 unless (eval { require Compress::Zlib }) {
  0         0  
679 0         0 $et->Warn('Install Compress::Zlib to read compressed metadata');
680 0         0 return 0;
681             }
682 0         0 my $inflate = Compress::Zlib::inflateInit();
683 0 0       0 $inflate and ($$dataPt, $stat) = $inflate->inflate($$dataPt);
684 0 0 0     0 unless ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
685 0         0 $et->Warn('Error uncompressing metadata');
686 0         0 return 0;
687             }
688 0         0 return 1;
689             }
690              
691             #------------------------------------------------------------------------------
692             # Brotli uncompress data
693             # Inputs: 0) ExifTool ref, 1) data ref
694             # Returns: true on success
695             sub Unbrotli($$)
696             {
697 0     0 0 0 my ($et, $dataPt) = @_;
698 0 0       0 unless (eval { require IO::Uncompress::Brotli }) {
  0         0  
699 0         0 $et->Warn('Install IO::Compress::Brotli to decode Brotli-compressed metadata');
700 0         0 return 0;
701             }
702 0         0 eval { $$dataPt = IO::Uncompress::Brotli::unbro($$dataPt, 100000000) };
  0         0  
703 0 0       0 if ($@) {
704 0         0 $et->Warn('Error decoding metadata');
705 0         0 $et->Warn('Try updating to IO::Uncompress::Brotli 0.004 or later');
706 0         0 return 0;
707             }
708 0         0 return 1;
709             }
710              
711             #------------------------------------------------------------------------------
712             # Read information from WOFF/WOFF2 font files
713             # Inputs: 0) ExifTool ref, 1) dirInfo ref
714             # Returns: 1 on success, 0 if this wasn't a recognized WOFF file
715             sub ProcessWOFF($$)
716             {
717 0     0 0 0 my ($et, $dirInfo) = @_;
718 0         0 my $raf = $$dirInfo{RAF};
719 0         0 my ($buff, $tbl, $i);
720 0 0 0     0 $raf->Seek(0,0) and $raf->Read($buff,48) == 48 or return 0;
721 0 0       0 $buff =~ /^(wOF[F2])/ or return 0;
722 0 0       0 my ($type, $off) = $1 eq 'wOFF' ? ('woff' , 20) : ('woff2', 24);
723 0         0 $et->SetFileType(uc($type), "font/$type");
724 0         0 SetByteOrder('MM');
725 0         0 my $flavor = substr($buff, 4, 4);
726 0         0 my $numTables = Get16u(\$buff, 12);
727 0         0 my ($vh, $vl, $metaPos, $metaLen) = unpack("x${off}nnNN", $buff);
728 0         0 my $verbose = $et->Options('Verbose');
729 0         0 my $tagTablePtr = GetTagTable('Image::ExifTool::Font::Main');
730 0         0 $et->HandleTag($tagTablePtr, WOFFVersion => "$vh.$vl");
731             #
732             # read font table
733             #
734 0 0       0 if ($type eq 'woff') {
735 0 0 0     0 unless ($raf->Seek($off+24,0) and $raf->Read($tbl,$numTables*20)==$numTables*20) {
736 0         0 $et->Warn('Error reading font table');
737 0         0 return 1;
738             }
739 0         0 for ($i=0; $i<$numTables; ++$i) {
740 0         0 my $pt = $i * 20;
741 0         0 my ($tag, $pos, $compLen, $len) = unpack("x${pt}a4N3", $tbl);
742 0 0 0     0 next unless $processTag{$tag} or $verbose;
743 0 0 0     0 $raf->Seek($pos,0) and $raf->Read($buff,$compLen)==$compLen or $et->Warn('Bad font table entry'), return 1;
744 0         0 my $dataPos;
745 0 0       0 if ($compLen eq $len) {
746 0         0 $dataPos = $pos;
747             } else {
748 0 0       0 next unless Uncompress($et, \$buff);
749             }
750 0         0 my $err = ProcessTableEntry($et, $i, $tag, \$buff, $dataPos);
751 0 0       0 $err and $et->Warn($err), return 1;
752 0 0       0 last if defined $err;
753             }
754             } else { # WOFF2
755 0         0 my $compSize = Get32u(\$buff, 20);
756 0         0 my ($err, @entry, $entry);
757 0 0       0 $raf->Seek($off+24,0) or $et->Warn('Error seeking to font table'), return 1;
758 0         0 for ($i=0; $i<$numTables; ++$i) {
759 0 0       0 $raf->Read($buff, 1) or $err = 1, last;
760 0         0 my $flags = unpack('C', $buff);
761 0         0 my $tag = $knownTags[$flags & 0x3f];
762 0 0 0     0 $tag or $raf->Read($tag, 4) == 4 or $err = 1, last;
763 0         0 my $len = ReadUIntBase128($raf);
764 0 0       0 defined $len or $err = 1, last;
765 0         0 my $transformed;
766 0 0 0     0 if (($tag eq 'glyf' or $tag eq 'loca') xor $flags & 0xc0) {
767             # a non-null transform was used
768 0         0 $len = ReadUIntBase128($raf);
769 0         0 $transformed = 1;
770             }
771             # save information about this entry for later
772 0         0 push @entry, [ $i, $tag, $len, $transformed ];
773             }
774 0 0       0 $err and $et->Warn('Error reading font table'), return 1;
775             # skip the collection table if necessary
776 0 0       0 if ($flavor eq 'ttcf') {
777 0 0       0 $raf->Seek(4, 1) or $et->Warn('Seek error'), return 1;
778 0         0 my $n = Read255UInt16($raf);
779 0 0 0     0 defined $n and $raf->Seek(4,1) or $et->Warn('Error reading collection table'), return 1;
780 0 0       0 $raf->Seek(4, 1) or $err = 1, last;
781 0         0 for ($i=0; $i<$n; ++$i) {
782 0 0       0 defined Read255UInt16($raf) or $err = 1, last;
783             }
784 0 0       0 $err and $et->Warn('Error reading collection directory'), return 1;
785             }
786 0 0       0 $raf->Read($buff,$compSize) == $compSize or $et->Warn('Error reading font data'), return 1;
787 0 0       0 return 1 unless Unbrotli($et, \$buff);
788             # after all that exhausting and frankly unnecessary work (poor file design),
789             # we finally have the uncompressed font data so we can process the table entries
790 0         0 my $pos = 0;
791 0         0 foreach $entry (@entry) {
792 0         0 my ($i, $tag, $len, $transformed) = @$entry;
793 0 0 0     0 if ($processTag{$tag} or $verbose) {
794 0         0 my $dat = substr($buff, $pos, $len);
795 0         0 my $err = ProcessTableEntry($et, $i, $tag, \$dat, undef, $transformed);
796 0 0       0 $err and $et->Warn($err), return 1;
797 0 0       0 last if defined $err;
798             }
799 0         0 $pos += $len;
800             }
801             }
802             #
803             # read compressed XML-format metadata (NC)
804             #
805 0 0       0 if ($metaLen) {
806 0 0 0     0 unless ($raf->Seek($metaPos,0) and $raf->Read($buff,$metaLen)==$metaLen) {
807 0         0 $et->Warn('Error reading metadata');
808 0         0 return 1;
809             }
810 0 0       0 if ($type eq 'woff') {
811 0 0       0 return 1 unless Uncompress($et, \$buff);
812             } else { # WOFF2
813 0 0       0 return 1 unless Unbrotli($et, \$buff);
814             }
815             # (we don't properly support XML structures)
816 0         0 my $oldStruct = $et->Options('Struct');
817 0         0 $et->Options(Struct => 0);
818 0         0 $et->HandleTag($tagTablePtr, 'XML', $buff);
819 0         0 $et->Options(Struct => $oldStruct);
820             }
821 0         0 return 1;
822             }
823              
824             #------------------------------------------------------------------------------
825             # Read information from various format font files
826             # Inputs: 0) ExifTool ref, 1) dirInfo ref
827             # Returns: 1 on success, 0 if this wasn't a recognized Font file
828             sub ProcessFont($$)
829             {
830 5     5 0 15 my ($et, $dirInfo) = @_;
831 5         18 my $raf = $$dirInfo{RAF};
832 5         8 my ($buff, $buf2, $rtnVal);
833 5 50 33     43 return 0 unless $raf->Read($buff, 24) and $raf->Seek(0,0);
834 5 100 33     89 if ($buff =~ /^(\0\x01\0\0|OTTO|true|typ1)[\0\x01]/) { # OTF, TTF
    50 33        
    100 33        
    100 33        
    50 33        
    0 33        
      33        
      33        
      33        
835 1         5 $rtnVal = ProcessOTF($et, $dirInfo);
836             } elsif ($buff =~ /^ttcf\0[\x01\x02]\0\0/) { # TTC
837 0         0 $rtnVal = ProcessTTC($et, $dirInfo);
838             } elsif ($buff =~ /^Start(Comp|Master)?FontMetrics\s+\d+/s) { # AFM
839 1         6 $rtnVal = ProcessAFM($et, $dirInfo);
840             } elsif ($buff =~ /^(.{6})?%!(PS-(AdobeFont-|Bitstream )|FontType1-)/s) {# PFA, PFB
841 2 100 50     19 $raf->Seek(6,0) and $et->SetFileType('PFB') if $1;
842 2         16 require Image::ExifTool::PostScript;
843 2         15 $rtnVal = Image::ExifTool::PostScript::ProcessPS($et, $dirInfo);
844             } elsif ($buff =~ /^\0[\x01\x02]/ and $raf->Seek(0, 2) and # PFM
845             # validate file size
846             $raf->Tell() > 117 and $raf->Tell() == unpack('x2V',$buff) and
847             # read PFM header
848             $raf->Seek(0,0) and $raf->Read($buff,117) == 117 and
849             # validate "DeviceType" string (must be "PostScript\0")
850             SetByteOrder('II') and $raf->Seek(Get32u(\$buff, 101), 0) and
851             # the DeviceType should be "PostScript\0", but FontForge
852             # incorrectly writes "Postscript\0", so ignore case
853             $raf->Read($buf2, 11) == 11 and lc($buf2) eq "postscript\0")
854             {
855 1         11 $et->SetFileType('PFM');
856 1 50 33     10 return 1 if $$et{OPTIONS}{FastScan} and $$et{OPTIONS}{FastScan} == 3;
857 1         6 SetByteOrder('II');
858 1         5 my $tagTablePtr = GetTagTable('Image::ExifTool::Font::Main');
859             # process the PFM header
860 1         10 $et->HandleTag($tagTablePtr, 'PFM', $buff);
861             # extract the font names
862 1         5 my $nameOff = Get32u(\$buff, 105);
863 1 50 33     6 if ($raf->Seek($nameOff, 0) and $raf->Read($buff, 256) and
      33        
864             $buff =~ /^([\x20-\xff]+)\0([\x20-\xff]+)\0/)
865             {
866 1         6 $et->HandleTag($tagTablePtr, 'fontname', $1);
867 1         3 $et->HandleTag($tagTablePtr, 'postfont', $2);
868             }
869 1         5 $rtnVal = 1;
870             } elsif ($buff =~ /^(wOF[F2])/) {
871 0         0 $rtnVal = ProcessWOFF($et, $dirInfo);
872             } else {
873 0         0 $rtnVal = 0;
874             }
875 5         27 return $rtnVal;
876             }
877              
878             1; # end
879              
880             __END__