///|
// Prawn's current font (`font.rb`): a family in a style at a size, set by
// `font` and `font_size` and looked up as Prawn's `find_font` does.
///|
/// A style of a font family (Prawn's `:normal`, `:bold`, `:italic`,
/// `:bold_italic`).
pub(all) enum FontStyle {
Normal
Bold
Italic
BoldItalic
}
///|
fn FontStyle::flags(self : FontStyle) -> (Bool, Bool) {
match self {
Normal => (false, false)
Bold => (true, false)
Italic => (false, true)
BoldItalic => (true, true)
}
}
///|
/// Prawn's `font(name, style:, size:)`: the font text is set in from now on
/// (within `body` only, when given), `name` being a family (in `style`, by
/// default its normal one) or a font of its own (a standard font's name,
/// such as `Times-Bold`). Raises UnknownFont for a name Prawn does not know.
pub fn Document::font(
self : Document,
name : String,
style? : FontStyle = Normal,
size? : Double,
body? : () -> Unit raise,
) -> Unit raise {
let (family, face_style, known) = self.find_font(name, style)
let key = self.catalog_key(family, face_style)
match body {
None => {
self.family = family
self.face_style = face_style
self.font_family_known = known
self.font_key = key
self.size = size.unwrap_or(self.size)
}
Some(body) => {
// Prawn's `save_font`, which restores whatever happens
let saved = (
self.family,
self.face_style,
self.size,
self.font_family_known,
self.font_key,
)
defer {
self.family = saved.0
self.face_style = saved.1
self.size = saved.2
self.font_family_known = saved.3
self.font_key = saved.4
}
self.family = family
self.face_style = face_style
self.font_family_known = known
self.font_key = key
self.size = size.unwrap_or(self.size)
body()
}
}
}
///|
/// Prawn's `find_font`: a family the document knows, in `style`; else a
/// standard font by its own name.
fn Document::find_font(
self : Document,
name : String,
style : FontStyle,
) -> (String, (Bool, Bool), Bool) raise PrawnError {
// a family registered by its files, in `style` (read when first used)
if self.families.contains(name) {
self.ensure_font(name, style.flags())
return (name, style.flags(), true)
}
if standard_families.iter().any(entry => entry.0 == name) {
return (name, style.flags(), true)
}
// Prawn's other built-in fonts, which pagelayout does not draw yet
if name == "Symbol" || name == "ZapfDingbats" {
raise Unsupported("the \{name} font")
}
// a face by its own name: drawn in that face, but of no family
match standard_face(name) {
Some((family, bold, italic)) => (family, (bold, italic), false)
// a font file of its own: Prawn's TTF font of no family, named by its
// path
None if is_font_file(name) => {
self.load_file(name)
(name, (false, false), false)
}
None => raise UnknownFont("\{name} is not a known font.")
}
}
///|
/// Prawn's `font_size` (without a size): the size text is set at.
pub fn Document::font_size(self : Document) -> Double {
self.size
}
///|
/// Prawn's `font_size(points) { … }`: `body` with text set at `points`,
/// then back to the size before (only when `body` returns: Prawn has no
/// `ensure` here).
pub fn Document::with_font_size(
self : Document,
points : Double,
body : () -> Unit raise,
) -> Unit raise {
let before = self.size
self.size = points
body()
self.size = before
}
///|
/// The current font's name (Prawn's `font.name`): a standard font's own
/// name (`Times-Bold`).
pub fn Document::font_name(self : Document) -> String {
// a TrueType font: its file
let (family, bold, italic) = self.font_key
if is_font_file(family) {
return family
}
for entry in standard_families {
if entry.0 == family {
let index = (if bold { 1 } else { 0 }) + (if italic { 2 } else { 0 })
return entry.1.get(index).unwrap_or(entry.1[0])
}
}
family
}
///|
/// The Ruby class of the current font (Prawn's `font.class`): AFM for a
/// standard font, TTF for one read from a file.
pub fn Document::font_class(self : Document) -> String {
let (bold, italic) = self.face_style
if self.face_of({ ..self.style(), bold, italic, }).standard {
"Prawn::Fonts::AFM"
} else {
"Prawn::Fonts::TTF"
}
}
///|
/// What `apply_style` saves of the current font.
priv struct SavedFont {
face_style : (Bool, Bool)
font_key : (String, Bool, Bool)
}
///|
/// The current font in `style`, when given (Prawn's text `:style` option:
/// `font(font.family, style:)`), raising `no_family` for a font of no
/// family; what it was before, for `restore_style`.
fn Document::apply_style(
self : Document,
style : FontStyle?,
no_family : PrawnError,
) -> SavedFont raise PrawnError {
let saved = { face_style: self.face_style, font_key: self.font_key, }
match style {
Some(style) => {
if !self.font_family_known {
raise no_family
}
self.ensure_font(self.family, style.flags())
self.face_style = style.flags()
self.font_key = self.catalog_key(self.family, style.flags())
}
None => ()
}
saved
}
///|
fn Document::restore_style(self : Document, saved : SavedFont) -> Unit {
self.face_style = saved.face_style
self.font_key = saved.font_key
}