1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
use std::fmt;
use crate::{data_structures::Rectangle, error::PdfResult, objects::Dictionary, pdf_enum, Resolve};
use super::embedded::{TrueTypeFontFile, Type1FontFile, Type3FontFile};
#[derive(Debug)]
pub struct FontDescriptor {
/// The PostScript name of the font. This name shall be the same as the value of
/// BaseFont in the font or CIDFont dictionary that refers to this font descriptor
font_name: String,
/// A byte string specifying the preferred font family name
font_family: Option<String>,
/// The font stretch value
///
/// The specific interpretation of this value varies from font to font
font_stretch: Option<FontStretch>,
/// The weight (thickness) component of the fully-qualified font name or font
/// specifier
///
/// The specific interpretation of these values varies from font to font
///
/// N.B.: Although the possible values of font weight are all positive integers, it is possible
/// for this value to be a float
font_weight: Option<FontWeight>,
/// A collection of flags defining various characteristics of the font
flags: FontDescriptorFlags,
/// A rectangle, expressed in the glyph coordinate system, that shall specify
/// the font bounding box. This should be the smallest rectangle enclosing the
/// shape that would result if all of the glyphs of the font were placed with
/// their origins coincident and then filled
font_bounding_box: Option<Rectangle>,
/// The angle, expressed in degrees counterclockwise from the vertical, of the dominant
/// vertical strokes of the font.
///
/// EXAMPLE 4 The 9-o’clock position is 90 degrees, and the 3-o’clock position is –90 degrees.
///
/// The value shall be negative for fonts that slope to the right, as almost all italic fonts do
italic_angle: f32,
/// The maximum height above the baseline reached by glyphs in this font. The height of
/// glyphs for accented characters shall be excluded
ascent: Option<f32>,
/// The maximum depth below the baseline reached by glyphs in this font
///
/// The value shall be a negative number
descent: Option<f32>,
/// The spacing between baselines of consecutive lines of text.
///
/// Default value: 0
leading: f32,
/// The vertical coordinate of the top of flat capital letters, measured from the baseline
cap_height: Option<f32>,
/// The font’s x height: the vertical coordinate of the top of flat nonascending lowercase
/// letters (like the letter x), measured from the baseline, in fonts that have Latin characters
///
/// Default value: 0
x_height: f32,
/// The thickness, measured horizontally, of the dominant vertical stems of glyphs in the font
stem_v: Option<f32>,
/// The thickness, measured vertically, of the dominant horizontal stems of glyphs in the font
///
/// Default value: 0
stem_h: f32,
/// The average width of glyphs in the font
///
/// Default value: 0
avg_width: f32,
/// The maximum width of glyphs in the font
///
/// Default value: 0
max_width: f32,
/// The width to use for character codes whose widths are not specified in a font dictionary’s
/// Widths array. This shall have a predictable effect only if all such codes map to glyphs whose
/// actual widths are the same as the value of the MissingWidth entry
///
/// Default value: 0
missing_width: f32,
/// A stream containing a Type 1 font program
font_file: Option<Type1FontFile>,
/// A stream containing a TrueType font program
font_file_two: Option<TrueTypeFontFile>,
/// A stream containing a font program whose format is specified by the Subtype entry in the
/// stream dictionary
font_file_three: Option<Type3FontFile>,
/// A string listing the character names defined in a font subset. The names in this string
/// shall be in PDF syntax—that is, each name preceded by a slash (/). The names may appear in
/// any order. The name .notdef shall be omitted; it shall exist in the font subset. If this entry
/// is absent, the only indication of a font subset shall be the subset tag in the FontName entry
///
/// Meaningful only in Type 1 fonts
charset: Option<String>,
}
#[derive(Debug)]
struct CidFontDescriptor {
base: FontDescriptor,
}
impl FontDescriptor {
const TYPE: &'static str = "FontDescriptor";
pub fn from_dict(mut dict: Dictionary, resolver: &mut dyn Resolve) -> PdfResult<Self> {
dict.expect_type(Self::TYPE, resolver, true)?;
let font_name = dict.expect_name("FontName", resolver)?;
let font_family = dict.get_string("FontFamily", resolver)?;
let font_stretch = dict
.get_name("FontStretch", resolver)?
.as_deref()
.map(FontStretch::from_str)
.transpose()?;
let font_weight = dict
.get_number("FontWeight", resolver)?
.map(|n| FontWeight::from_integer(n as i32))
.transpose()?;
let flags = FontDescriptorFlags(dict.expect_unsigned_integer("Flags", resolver)?);
let font_bounding_box = dict.get_rectangle("FontBBox", resolver)?;
let italic_angle = dict.expect_number("ItalicAngle", resolver)?;
let ascent = dict.get_number("Ascent", resolver)?;
let descent = dict.get_number("Descent", resolver)?;
let leading = dict.get_number("Leading", resolver)?.unwrap_or(0.0);
let cap_height = dict.get_number("CapHeight", resolver)?;
let x_height = dict.get_number("XHeight", resolver)?.unwrap_or(0.0);
let stem_v = dict.get_number("StemV", resolver)?;
let stem_h = dict.get_number("StemH", resolver)?.unwrap_or(0.0);
let avg_width = dict.get_number("AvgWidth", resolver)?.unwrap_or(0.0);
let max_width = dict.get_number("MaxWidth", resolver)?.unwrap_or(0.0);
let missing_width = dict.get_number("MissingWidth", resolver)?.unwrap_or(0.0);
let font_file = dict
.get_stream("FontFile", resolver)?
.map(|stream| Type1FontFile::from_stream(stream, resolver))
.transpose()?;
let font_file_two = dict
.get_stream("FontFile2", resolver)?
.map(|stream| TrueTypeFontFile::from_stream(stream, resolver))
.transpose()?;
let font_file_three = dict
.get_stream("FontFile3", resolver)?
.map(|stream| Type3FontFile::from_stream(stream, resolver))
.transpose()?;
let charset = dict.get_string("CharSet", resolver)?;
Ok(Self {
font_name,
font_family,
font_stretch,
font_weight,
flags,
font_bounding_box,
italic_angle,
ascent,
descent,
leading,
cap_height,
x_height,
stem_v,
stem_h,
avg_width,
max_width,
missing_width,
font_file,
font_file_two,
font_file_three,
charset,
})
}
}
#[derive(Clone, Copy)]
struct FontDescriptorFlags(u32);
impl fmt::Debug for FontDescriptorFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:b}", self.0)
}
}
impl FontDescriptorFlags {
const FIXED_PITCH: u32 = 1 << 0;
const SERIF: u32 = 1 << 1;
const SYMBOLIC: u32 = 1 << 2;
const SCRIPT: u32 = 1 << 3;
const NON_SYMBOLIC: u32 = 1 << 5;
const ITALIC: u32 = 1 << 6;
const ALL_CAP: u32 = 1 << 16;
const SMALL_CAP: u32 = 1 << 17;
const FORCE_BOLD: u32 = 1 << 18;
/// All glyphs have the same width (as opposed to proportional or variable-pitch
/// fonts, which have different widths).
pub const fn is_fixed_pitch(&self) -> bool {
self.0 & Self::FIXED_PITCH != 0
}
/// Glyphs have serifs, which are short strokes drawn at an angle on the top and
/// bottom of glyph stems. (Sans serif fonts do not have serifs.)
pub const fn is_serif(&self) -> bool {
self.0 & Self::SERIF != 0
}
/// Glyphs have serifs, which are short strokes drawn at an angle on the top and
/// bottom of glyph stems. (Sans serif fonts do not have serifs.)
pub const fn is_sans_serif(&self) -> bool {
self.0 & Self::SERIF == 0
}
/// Font contains glyphs outside the Adobe standard Latin character set.
///
/// This flag and the Nonsymbolic flag shall not both be set or both be clear
pub const fn is_symbolic(&self) -> bool {
self.0 & Self::SYMBOLIC != 0
}
/// Glyphs resemble cursive handwriting
pub const fn is_script(&self) -> bool {
self.0 & Self::SCRIPT != 0
}
/// Font uses the Adobe standard Latin character set or a subset of it
pub const fn is_non_symbolic(&self) -> bool {
self.0 & Self::NON_SYMBOLIC != 0
}
/// Glyphs have dominant vertical strokes that are slanted
pub const fn is_italic(&self) -> bool {
self.0 & Self::ITALIC != 0
}
/// Font contains no lowercase letters; typically used for display purposes,
/// such as for titles or headlines
pub const fn is_all_cap(&self) -> bool {
self.0 & Self::ALL_CAP != 0
}
/// Font contains both uppercase and lowercase letters. The uppercase letters
/// are similar to those in the regular version of the same typeface family.
/// The glyphs for the lowercase letters have the same shapes as the corresponding
/// uppercase letters, but they are sized and their proportions adjusted so that
/// they have the same size and stroke weight as lowercase glyphs in the same typeface
/// family
pub const fn is_small_cap(&self) -> bool {
self.0 & Self::SMALL_CAP != 0
}
}
pdf_enum!(
int
#[derive(Debug)]
enum FontWeight {
OneHundred = 100,
TwoHundred = 200,
ThreeHundred = 300,
/// Normal
FourHundred = 400,
FiveHundred = 500,
SixHundred = 600,
/// Bold
SevenHundred = 700,
EightHundred = 800,
NineHundred = 900,
}
);
pdf_enum!(
#[derive(Debug)]
enum FontStretch {
UltraCondensed = "UltraCondensed",
ExtraCondensed = "ExtraCondensed",
Condensed = "Condensed",
SemiCondensed = "SemiCondensed",
Normal = "Normal",
SemiExpanded = "SemiExpanded",
Expanded = "Expanded",
ExtraExpanded = "ExtraExpanded",
UltraExpanded = "UltraExpanded",
}
);