1use pom::char_class;
2use pom::Parser;
3use pom::parser::*;
4use std::str::FromStr;
5use std::string::*;
6use std::str;
7use std::char;
8
9use std::collections::HashMap;
10
11extern crate geo;
12extern crate pom;
13
14use geo::Bbox;
15
16#[derive(PartialEq, Debug)]
17pub struct FontMetrics {
18 pub metric_sets: i32,
19 pub font_name: String,
20 pub full_name: String,
21 pub family_name: String,
22 pub weight: String,
23 pub font_bbox: Bbox<f64>,
24 pub font_version: String,
25 pub notice: String,
26 pub encoding_scheme: String,
27 pub mapping_scheme: u32,
28 pub esc_char: u32,
29 pub character_set: String,
30 pub characters: u32,
31 pub is_base_font: bool,
32 pub v_vector: (f64, f64),
33 pub is_fixed_v: bool,
34 pub cap_height: f64,
35 pub x_height: f64,
36 pub ascender: f64,
37 pub descender: f64,
38 pub comments: Vec<String>,
39
40 pub underline_position: f64,
41 pub underline_thickness: f64,
42 pub italic_angle: f64,
43 pub char_width: (f64, f64),
44 pub is_fixed_pitch: bool,
45 pub standard_horizontal_width: f64,
46 pub standard_vertical_width: f64,
47
48 pub char_metrics: Vec<CharMetric>,
49 pub char_metrics_map: HashMap<String, CharMetric>,
50 pub track_kern: Vec<TrackKern>,
51 pub composites: Vec<Composite>,
52 pub kern_pairs: Vec<KernPair>,
53 pub kern_pairs0: Vec<KernPair>,
54 pub kern_pairs1: Vec<KernPair>,
55}
56
57impl Default for FontMetrics {
58 fn default() -> FontMetrics {
59 FontMetrics {
60 metric_sets: 0,
62 font_name: String::new(),
63 full_name: String::new(),
64 family_name: String::new(),
65 weight: String::new(),
66 font_bbox: Bbox {
67 xmin: 0.,
68 xmax: 0.,
69 ymin: 0.,
70 ymax: 0.,
71 },
72 font_version: String::new(),
73 notice: String::new(),
74 encoding_scheme: String::new(),
75 mapping_scheme: 0,
76 esc_char: 0,
77 character_set: String::new(),
78 characters: 0,
79 is_base_font: true,
80 v_vector: (0.0, 0.0),
81 is_fixed_v: true,
82 cap_height: 0.0,
83 x_height: 0.0,
84 ascender: 0.0,
85 descender: 0.0,
86 comments: Vec::new(),
87
88 underline_position: 0.0,
89 underline_thickness: 0.0,
90 italic_angle: 0.0,
91 char_width: (0.0, 0.0),
92 is_fixed_pitch: true,
93 standard_horizontal_width: 0.0,
94 standard_vertical_width: 0.0,
95
96 char_metrics: Vec::new(),
97 char_metrics_map: HashMap::new(),
98 track_kern: Vec::new(),
99 composites: Vec::new(),
100 kern_pairs: Vec::new(),
101 kern_pairs0: Vec::new(),
102 kern_pairs1: Vec::new(),
103 }
104 }
105}
106
107#[derive(PartialEq, Debug)]
108pub struct CharMetric {
109 pub name: String,
110 pub bbox: Bbox<f64>,
111 pub ligatures: Vec<Ligature>,
112 pub character_code: i32,
113 pub wx: f64,
114 pub w0x: f64,
115 pub w1x: f64,
116 pub wy: f64,
117 pub w0y: f64,
118 pub w1y: f64,
119 pub w: (f64, f64),
120 pub w0: (f64, f64),
121 pub w1: (f64, f64),
122 pub vv: (f64, f64),
123}
124
125impl Default for CharMetric {
126 fn default() -> CharMetric {
127 CharMetric {
128 name: String::new(),
129 bbox: Bbox {
130 xmin: 0.,
131 xmax: 0.,
132 ymin: 0.,
133 ymax: 0.,
134 },
135 ligatures: Vec::new(),
136 character_code: 0,
137 wx: 0.0,
138 w0x: 0.0,
139 w1x: 0.0,
140 wy: 0.0,
141 w0y: 0.0,
142 w1y: 0.0,
143 w: (0.0, 0.0),
144 w0: (0.0, 0.0),
145 w1: (0.0, 0.0),
146 vv: (0.0, 0.0),
147 }
148 }
149}
150
151#[derive(PartialEq, Debug)]
152pub struct TrackKern {
153 pub degree: i32,
154 pub min_point_size: f64,
155 pub min_kern: f64,
156 pub max_point_size: f64,
157 pub max_kern: f64,
158}
159
160#[derive(PartialEq, Debug)]
161pub struct KernPair {
162 pub first_kern_character: String,
163 pub second_kern_character: String,
164 pub x: f64,
165 pub y: f64,
166}
167
168#[derive(PartialEq, Debug)]
169pub struct Ligature {
170 pub successor: String,
171 pub ligature: String,
172}
173
174#[derive(PartialEq, Debug)]
175pub struct Composite {
176 pub name: String,
177 pub parts: Vec<CompositePart>,
178}
179
180#[derive(PartialEq, Debug)]
181pub struct CompositePart {
182 pub name: String,
183 pub x_displacement: i32,
184 pub y_displacement: i32,
185}
186
187fn string_char(c: u8) -> bool {
188 c >= 0x20 && c <= 0x7E
189}
190
191fn name_char(c: u8) -> bool {
192 string_char(c) && (!char_class::space(c))
193}
194
195fn digit(c: u8) -> bool {
196 c >= b'0' && c <= b'9'
197}
198
199fn space() -> Parser<u8, ()> {
200 is_a(char_class::space).repeat(1..).discard()
201}
202
203fn eol() -> Parser<u8, ()> {
204 (is_a(char_class::space).repeat(0..) - one_of(b"\r\n").repeat(1..)).discard()
205}
206
207fn string() -> Parser<u8, String> {
210 is_a(string_char).repeat(1..).convert(String::from_utf8)
211}
212
213fn name() -> Parser<u8, String> {
214 is_a(name_char).repeat(1..).convert(String::from_utf8)
215}
216
217fn boolean() -> Parser<u8, bool> {
218 seq(b"true").map(|_| true) | seq(b"false").map(|_| false)
219}
220
221fn integer() -> Parser<u8, i32> {
222 let integer =
223 sym(b'-').opt() - (one_of(b"123456789") - one_of(b"0123456789").repeat(0..) | sym(b'0'));
224 integer
225 .collect()
226 .convert(String::from_utf8)
227 .convert(|s| i32::from_str(&s))
228}
229
230fn uinteger() -> Parser<u8, u32> {
231 let integer = one_of(b"123456789") - one_of(b"0123456789").repeat(0..) | sym(b'0');
232 integer
233 .collect()
234 .convert(String::from_utf8)
235 .convert(|s| u32::from_str(&s))
236}
237
238fn hex_integer() -> Parser<u8, i32> {
239 let hex_digits = is_a(char_class::hex_digit).repeat(1..).collect();
240 sym(b'<') * hex_digits.convert(|v| i32::from_str_radix(&String::from_utf8(v).unwrap(), 16))
241}
242
243fn number() -> Parser<u8, f64> {
244 let integer = one_of(b"123456789") - one_of(b"0123456789").repeat(0..) | sym(b'0');
245 let frac = sym(b'.') + one_of(b"0123456789").repeat(1..);
246 let exp = one_of(b"eE") + one_of(b"+-").opt() + one_of(b"0123456789").repeat(1..);
247 let number = sym(b'-').opt() + integer + frac.opt() + exp.opt();
248 number
249 .collect()
250 .convert(String::from_utf8)
251 .convert(|s| f64::from_str(&s))
252}
253
254fn bbox() -> Parser<u8, Bbox<f64>> {
255 let numbers = (number() - space()).repeat(3) + number();
256 numbers.map(|(nums, num)| Bbox {
257 xmin: nums[0],
258 xmax: nums[1],
259 ymin: nums[2],
260 ymax: num,
261 })
262}
263
264fn start_command(command: &'static [u8]) -> Parser<u8, ()> {
267 (seq(command) * space()).discard()
268}
269
270fn string_command(
271 command: &'static [u8],
272 build: &'static Fn(String) -> Command,
273) -> Parser<u8, Command> {
274 start_command(command) * string().map(build)
275}
276
277fn integer_command(
278 command: &'static [u8],
279 build: &'static Fn(i32) -> Command,
280) -> Parser<u8, Command> {
281 start_command(command) * integer().map(build)
282}
283
284fn uinteger_command(
285 command: &'static [u8],
286 build: &'static Fn(u32) -> Command,
287) -> Parser<u8, Command> {
288 start_command(command) * uinteger().map(build)
289}
290
291fn number_command(
292 command: &'static [u8],
293 build: &'static Fn(f64) -> Command,
294) -> Parser<u8, Command> {
295 start_command(command) * number().map(build)
296}
297
298fn bool_command(
299 command: &'static [u8],
300 build: &'static Fn(bool) -> Command,
301) -> Parser<u8, Command> {
302 start_command(command) * boolean().map(build)
303}
304
305fn bbox_command(
306 command: &'static [u8],
307 build: &'static Fn(Bbox<f64>) -> Command,
308) -> Parser<u8, Command> {
309 start_command(command) * bbox().map(build)
310}
311
312fn num_num_command(
313 command: &'static [u8],
314 build: &'static Fn(f64, f64) -> Command,
315) -> Parser<u8, Command> {
316 start_command(command) * (number() - space() + number()).map(move |(a, b)| build(a, b))
317}
318
319fn kp_cmd() -> Parser<u8, KernPair> {
321 let cmd = seq(b"KP") - space();
322 let names = name() - space() + name() - space();
323 let nums = number() - space() + number();
324 cmd * (names + nums).map(move |((n1, n2), (numx, numy))| KernPair {
325 first_kern_character: n1,
326 second_kern_character: n2,
327 x: numx,
328 y: numy,
329 })
330}
331
332static ERROR_MSG: Result<String, &str> = Err("hex value not in ascii");
333
334fn hex_string() -> Parser<u8, String> {
335 let hex_bytes = is_a(char_class::hex_digit).repeat(2).collect();
336 let hex_int = hex_bytes.convert(|b| u32::from_str_radix(str::from_utf8(&b).unwrap(), 16));
337 let hex_char = hex_int.convert(|u| char::from_u32(u).ok_or(ERROR_MSG.clone()));
338 sym(b'<') * hex_char.repeat(0..).map(|v| v.into_iter().collect()) - sym(b'>')
339}
340
341fn kph_cmd() -> Parser<u8, KernPair> {
342 let cmd = seq(b"KPH") - space();
343 let names = hex_string() - space() + hex_string() - space();
344 let nums = number() - space() + number();
345 cmd * (names + nums).map(move |((n1, n2), (numx, numy))| KernPair {
346 first_kern_character: n1,
347 second_kern_character: n2,
348 x: numx,
349 y: numy,
350 })
351}
352
353fn kpx_cmd() -> Parser<u8, KernPair> {
354 let cmd = seq(b"KPX") - space();
355 let names = name() - space() + name() - space();
356 let num = number();
357 cmd * (names + num).map(|((name1, name2), num)| KernPair {
358 first_kern_character: name1,
359 second_kern_character: name2,
360 x: num,
361 y: 0.0,
362 })
363}
364
365fn kpy_cmd() -> Parser<u8, KernPair> {
366 let cmd = seq(b"KPY") - space();
367 let names = name() - space() + name() - space();
368 let num = number();
369 cmd * (names + num).map(|((name1, name2), num)| KernPair {
370 first_kern_character: name1,
371 second_kern_character: name2,
372 x: 0.0,
373 y: num,
374 })
375}
376
377fn kern_pair() -> Parser<u8, KernPair> {
378 kp_cmd() | kph_cmd() | kpx_cmd() | kpy_cmd()
379}
380
381fn kern_pairs() -> Parser<u8, (Option<u8>, Vec<KernPair>)> {
382 let command = seq(b"StartKernPairs") * one_of(b"01").opt();
383 command - space() + uinteger() - eol().repeat(1..) >> move |(idx, len)| {
384 let end = seq(b"EndKernPairs") - eol().repeat(1..);
385 let pairs = (kern_pair() - eol().repeat(1..)).repeat(len as usize);
386 pairs.map(move |pairs| (idx, pairs)) - end
387 }
388}
389
390fn track_kern() -> Parser<u8, TrackKern> {
391 let begin = seq(b"TrackKern") - space();
392 let num_space = || number() - space();
393 let content = integer() - space() + num_space() + num_space() + num_space() + num_space();
394 begin
395 * content.map(
396 |((((deg, min_size), min_kern), max_size), max_kern)| TrackKern {
397 degree: deg,
398 min_point_size: min_size,
399 min_kern: min_kern,
400 max_point_size: max_size,
401 max_kern: max_kern,
402 },
403 )
404}
405
406fn track_kerns() -> Parser<u8, Vec<TrackKern>> {
407 let begin = (seq(b"StartTrackKern") - space()) * uinteger() - eol();
408 begin >> move |len| (track_kern() - eol()).repeat(len as usize) - seq(b"EndTrackKern") - eol()
409}
410
411fn kern_data() -> Parser<u8, Vec<KernDataCmd>> {
412 let kernpairs = kern_pairs().map(|(idx, pairs)| match idx {
413 Some(0) => KernDataCmd::Kernpairs0(pairs),
414 Some(1) => KernDataCmd::Kernpairs1(pairs),
415 Some(_) => unreachable!(),
416 None => KernDataCmd::Kernpairs(pairs),
417 });
418 let trackkern = track_kerns().map(|kerns| KernDataCmd::TrackKern(kerns));
419 let begin = seq(b"StartKernData") - eol().repeat(1..);
420 let content = (kernpairs | trackkern).repeat(1..);
421 begin * content - seq(b"EndKernData")
422}
423
424fn composite_part() -> Parser<u8, CompositePart> {
427 let params =
428 (name() - space() + integer() - space() + integer()).map(|((n, x), y)| CompositePart {
429 name: n,
430 x_displacement: x,
431 y_displacement: y,
432 });
433 (seq(b"PCC") - space()) * params
434}
435
436fn composite() -> Parser<u8, Composite> {
437 (seq(b"CC") - space()) * name() - space() + uinteger() >> |(name, len): (String, u32)| {
438 (space() * composite_part())
439 .repeat(len as usize)
440 .map(move |parts| Composite {
441 name: name.to_owned(),
442 parts: parts,
443 })
444 }
445}
446
447fn composites() -> Parser<u8, Vec<Composite>> {
448 (seq(b"StartComposites") - space()) * uinteger() - eol().repeat(1..) >> move |len| {
449 (composite() - eol().repeat(1..)).repeat(len as usize) - seq(b"EndComposites")
450 }
451}
452
453#[derive(PartialEq, Debug)]
456enum CharMetricCommand {
457 C(i32),
458 WX(f64),
459 W0X(f64),
460 W1X(f64),
461 WY(f64),
462 W0Y(f64),
463 W1Y(f64),
464 W(f64, f64),
465 W0(f64, f64),
466 W1(f64, f64),
467 VV(f64, f64),
468 N(String),
469 B(Bbox<f64>),
470 L(Ligature),
471}
472
473fn ligature() -> Parser<u8, Ligature> {
474 (name() - space() + name()).map(|(s, l)| Ligature {
475 successor: s,
476 ligature: l,
477 })
478}
479
480fn charcommand() -> Parser<u8, CharMetricCommand> {
481 (sym(b'C') - space()) * integer().map(&CharMetricCommand::C)
482 | (seq(b"CH") - space()) * hex_integer().map(&CharMetricCommand::C)
483 | (seq(b"WX") - space()) * number().map(&CharMetricCommand::WX)
484 | (seq(b"W0X") - space()) * number().map(&CharMetricCommand::W0X)
485 | (seq(b"W1X") - space()) * number().map(&CharMetricCommand::W1X)
486 | (seq(b"WY") - space()) * number().map(&CharMetricCommand::WY)
487 | (seq(b"W0Y") - space()) * number().map(&CharMetricCommand::W0Y)
488 | (seq(b"W1Y") - space()) * number().map(&CharMetricCommand::W1Y)
489 | (seq(b"W") - space())
490 * (number() - space() + number()).map(|(x, y)| CharMetricCommand::W(x, y))
491 | (seq(b"W0") - space())
492 * (number() - space() + number()).map(|(x, y)| CharMetricCommand::W0(x, y))
493 | (seq(b"W1") - space())
494 * (number() - space() + number()).map(|(x, y)| CharMetricCommand::W1(x, y))
495 | (seq(b"VV") - space())
496 * (number() - space() + number()).map(|(x, y)| CharMetricCommand::VV(x, y))
497 | (sym(b'N') - space()) * name().map(CharMetricCommand::N)
498 | (sym(b'B') - space()) * bbox().map(CharMetricCommand::B)
499 | (sym(b'L') - space()) * ligature().map(CharMetricCommand::L)
500}
501
502fn char_metric() -> Parser<u8, CharMetric> {
503 let seperator = || space().opt() * sym(b';') - space().opt();
504 let cmds = list(charcommand(), seperator()) - seperator().opt();
505 cmds.map(|commands| {
506 commands.into_iter().fold(
507 CharMetric::default(),
508 |mut metric: CharMetric, command: CharMetricCommand| {
509 match command {
510 CharMetricCommand::C(c) => metric.character_code = c,
511 CharMetricCommand::WX(wx) => metric.wx = wx,
512 CharMetricCommand::W0X(w0x) => metric.w0x = w0x,
513 CharMetricCommand::W1X(w1x) => metric.w1x = w1x,
514 CharMetricCommand::WY(wy) => metric.wy = wy,
515 CharMetricCommand::W0Y(w0y) => metric.w0y = w0y,
516 CharMetricCommand::W1Y(w1y) => metric.w1y = w1y,
517 CharMetricCommand::W(w1, w2) => metric.w = (w1, w2),
518 CharMetricCommand::W0(w1, w2) => metric.w0 = (w1, w2),
519 CharMetricCommand::W1(w1, w2) => metric.w1 = (w1, w2),
520 CharMetricCommand::VV(vv1, vv2) => metric.vv = (vv1, vv2),
521 CharMetricCommand::N(name) => metric.name = name,
522 CharMetricCommand::B(bbox) => metric.bbox = bbox,
523 CharMetricCommand::L(lig) => metric.ligatures.push(lig),
524 }
525 metric
526 },
527 )
528 })
529}
530
531fn char_metrics() -> Parser<u8, Vec<CharMetric>> {
532 let begin = seq(b"StartCharMetrics") * space() * uinteger() - eol().repeat(1..);
533 begin >> move |len: u32| {
534 (char_metric() - eol().repeat(1..)).repeat(len as usize) - seq(b"EndCharMetrics")
535 }
536}
537
538fn comment() -> Parser<u8, Command> {
539 let cmd =
540 (seq(b"Comment") - space()) * string().opt().map(|o| o.unwrap_or_else(|| String::new()));
541 cmd.map(Command::Comment)
542}
543
544#[derive(PartialEq, Debug)]
547enum Command {
548 MetricsSet(i32),
550 FontName(String),
551 FullName(String),
552 FamilyName(String),
553 Weight(String),
554 FontBBox(Bbox<f64>),
555 Version(String),
556 Notice(String),
557 EncodingScheme(String),
558 MappingScheme(u32),
559 EscChar(u32),
560 CharacterSet(String),
561 Characters(u32),
562 IsBaseFont(bool),
563 VVector(f64, f64),
564 IsFixedV(bool),
565 CapHeight(f64),
566 XHeight(f64),
567 Ascender(f64),
568 Descender(f64),
569 StdHW(f64),
570 StdVW(f64),
571 Comment(String),
572 UnderlinePosition(f64),
573 UnderlineThickness(f64),
574 ItalicAngle(f64),
575 CharWidth(f64, f64),
576 IsFixedPitch(bool),
577 CharMetrics(Vec<CharMetric>),
578 Composites(Vec<Composite>),
579 KernData(Vec<KernDataCmd>),
580}
581
582#[derive(PartialEq, Debug)]
583enum KernDataCmd {
584 TrackKern(Vec<TrackKern>),
585 Kernpairs(Vec<KernPair>),
586 Kernpairs0(Vec<KernPair>),
587 Kernpairs1(Vec<KernPair>),
588}
589
590fn command<'a>() -> Parser<u8, Command> {
591 comment() | string_command(b"Version", &Command::Version)
592 | integer_command(b"MetricsSet", &Command::MetricsSet)
593 | string_command(b"FontName", &Command::FontName)
594 | string_command(b"FullName", &Command::FullName)
595 | string_command(b"FamilyName", &Command::FamilyName)
596 | string_command(b"Weight", &Command::Weight)
597 | bbox_command(b"FontBBox", &Command::FontBBox)
598 | string_command(b"Weight", &Command::Weight)
599 | string_command(b"Version", &Command::Version)
600 | string_command(b"Notice", &Command::Notice)
601 | string_command(b"EncodingScheme", &Command::EncodingScheme)
602 | uinteger_command(b"MappingScheme", &Command::MappingScheme)
603 | uinteger_command(b"EscChar", &Command::EscChar)
604 | string_command(b"CharacterSet", &Command::CharacterSet)
605 | uinteger_command(b"Characters", &Command::Characters)
606 | bool_command(b"IsBaseFont", &Command::IsBaseFont)
607 | num_num_command(b"VVector", &Command::VVector)
608 | bool_command(b"IsFixedV", &Command::IsFixedV)
609 | number_command(b"CapHeight", &Command::CapHeight)
610 | number_command(b"XHeight", &Command::XHeight)
611 | number_command(b"Ascender", &Command::Ascender)
612 | number_command(b"Descender", &Command::Descender)
613 | number_command(b"StdHW", &Command::StdHW) | number_command(b"StdVW", &Command::StdVW)
614 | number_command(b"UnderlinePosition", &Command::UnderlinePosition)
615 | number_command(b"UnderlineThickness", &Command::UnderlineThickness)
616 | number_command(b"ItalicAngle", &Command::ItalicAngle)
617 | num_num_command(b"CharWidth", &Command::CharWidth)
618 | bool_command(b"IsFixedPitch", &Command::IsFixedPitch)
619 | char_metrics().map(Command::CharMetrics) | composites().map(Command::Composites)
620 | kern_data().map(Command::KernData)
621}
622
623pub fn afm() -> Parser<u8, FontMetrics> {
626 let begin =
627 start_command(b"StartFontMetrics") * (is_a(digit) - sym(b'.') + is_a(digit)) - eol();
628 let end = eol().opt() * seq(b"EndFontMetrics") * eol().repeat(0..) * end();
629 let elems = list(command(), eol());
630
631 let commands = begin * elems.expect("AFM commands") - end.expect("EndFontMetrics");
632 commands.map(|commands| {
633 commands.into_iter().fold(
634 FontMetrics::default(),
635 |mut metric: FontMetrics, command: Command| {
636 match command {
637 Command::MetricsSet(metric_sets) => metric.metric_sets = metric_sets,
638 Command::FontName(name) => metric.font_name = name,
639 Command::FullName(name) => metric.full_name = name,
640 Command::FamilyName(name) => metric.family_name = name,
641 Command::Weight(weight) => metric.weight = weight,
642 Command::FontBBox(bbox) => metric.font_bbox = bbox,
643 Command::Version(version) => metric.font_version = version,
644 Command::Notice(notice) => metric.notice = notice,
645 Command::EncodingScheme(scheme) => metric.encoding_scheme = scheme,
646 Command::MappingScheme(scheme) => metric.mapping_scheme = scheme,
647 Command::EscChar(c) => metric.esc_char = c,
648 Command::CharacterSet(charset) => metric.character_set = charset,
649 Command::Characters(c) => metric.characters = c,
650 Command::IsBaseFont(base_font) => metric.is_base_font = base_font,
651 Command::VVector(v1, v2) => metric.v_vector = (v1, v2),
652 Command::IsFixedV(fixed) => metric.is_fixed_v = fixed,
653 Command::CapHeight(height) => metric.cap_height = height,
654 Command::XHeight(height) => metric.x_height = height,
655 Command::Ascender(asc) => metric.ascender = asc,
656 Command::Descender(desc) => metric.descender = desc,
657 Command::StdHW(stdhw) => metric.standard_horizontal_width = stdhw,
658 Command::StdVW(stdvw) => metric.standard_vertical_width = stdvw,
659 Command::Comment(comment) => metric.comments.push(comment),
660 Command::UnderlinePosition(pos) => metric.underline_position = pos,
661 Command::UnderlineThickness(thickness) => {
662 metric.underline_thickness = thickness
663 }
664 Command::ItalicAngle(angle) => metric.italic_angle = angle,
665 Command::CharWidth(w1, w2) => metric.char_width = (w1, w2),
666 Command::IsFixedPitch(fixed) => metric.is_fixed_pitch = fixed,
667 Command::CharMetrics(char_metrics) => metric.char_metrics = char_metrics,
668 Command::Composites(composites) => metric.composites = composites,
669 Command::KernData(cmds) => for cmd in cmds {
670 match cmd {
671 KernDataCmd::TrackKern(kerns) => metric.track_kern = kerns,
672 KernDataCmd::Kernpairs(pairs) => metric.kern_pairs = pairs,
673 KernDataCmd::Kernpairs0(pairs) => metric.kern_pairs0 = pairs,
674 KernDataCmd::Kernpairs1(pairs) => metric.kern_pairs1 = pairs,
675 }
676 },
677 }
678 metric
679 },
680 )
681 })
682}
683
684#[cfg(test)]
685mod tests {
686 use super::*;
687 use pom::DataInput;
688 use std::path::Path;
689 use std::fs::File;
690 use std::io::prelude::*;
691
692 #[test]
693 fn parse_demo_file() {
694 let input = br#"StartFontMetrics 4.1
695Comment UniqueID 43050
696Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
697Comment Creation Date: Thu May 1 17:27:09 1997
698Comment VMusage 39754 50779
699FontName Courier
700FullName Courier
701FamilyName Courier
702Weight Medium
703ItalicAngle 0
704IsFixedPitch true
705CharacterSet ExtendedRoman
706FontBBox -23 -250 715 805
707UnderlinePosition -100
708UnderlineThickness 50
709Version 003.000
710Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
711EncodingScheme AdobeStandardEncoding
712CapHeight 562
713XHeight 426
714Ascender 629
715Descender -157
716StdHW 51
717StdVW 51
718StartCharMetrics 5
719C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
720C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ;
721C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ;
722C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ;
723C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ;
724EndCharMetrics
725StartKernData
726StartKernPairs 3
727KPX A Cacute -30
728KPX ecaron period -15
729KPX zdotaccent ohungarumlaut -15
730EndKernPairs
731EndKernData
732EndFontMetrics
733"#;
734 let mut buf = DataInput::new(input);
735 let parser = afm().parse(&mut buf);
736 assert!(parser.is_ok());
737 let r = parser.unwrap();
738 assert_eq!(r.font_name, "Courier");
739 assert_eq!(r.family_name, "Courier");
740 assert_eq!(r.full_name, "Courier");
741 assert_eq!(r.font_version, "003.000");
742 assert_eq!(r.comments[0], "UniqueID 43050");
743 assert_eq!(r.comments.len(), 4);
744 assert_eq!(r.weight, "Medium");
745 assert_eq!(r.italic_angle, 0.0);
746 assert_eq!(r.is_fixed_pitch, true);
747 assert_eq!(r.character_set, "ExtendedRoman");
748 assert_eq!(r.underline_position, -100.0);
749 assert_eq!(r.underline_thickness, 50.0);
750 assert_eq!(r.font_version, "003.000");
751 assert_eq!(r.notice.len(), 98);
752 assert_eq!(r.cap_height, 562.0);
753 assert_eq!(r.x_height, 426.0);
754 assert_eq!(r.ascender, 629.0);
755 assert_eq!(r.descender, -157.0);
756 assert_eq!(r.standard_horizontal_width, 51.0);
757 assert_eq!(r.standard_vertical_width, 51.0);
758 assert_eq!(r.char_metrics.len(), 5);
759 assert_eq!(r.kern_pairs.len(), 3);
760 }
761
762 #[test]
763 fn parse_standard_14_pdf_fonts() {
764 use std::fs;
765 use std::ffi::OsStr;
766
767 let assets_dir = Path::new("assets");
768 for file in fs::read_dir(assets_dir).unwrap() {
769 let path = file.expect("I/O error when reading a asset").path();
770 if path.is_file() && path.extension() == Some(OsStr::new("afm")) {
771 println!("Parse file {}", path.display());
772 let mut file = File::open(&path).expect("Could not open an asset file");
773 let mut v = Vec::new();
774 file.read_to_end(&mut v)
775 .expect(&format!("Could not read {}", path.display()));
776
777 let mut buf = DataInput::new(&v);
778 let parse_result = afm().parse(&mut buf);
779 assert!(
780 parse_result.is_ok(),
781 "Could not parse the asset file {}",
782 path.display()
783 );
784 }
785 }
786 }
787}