Skip to content

Commit

Permalink
move AFM fonts into separate crate
Browse files Browse the repository at this point in the history
  • Loading branch information
rkusa committed Aug 9, 2020
1 parent 5828ede commit 4e7405e
Show file tree
Hide file tree
Showing 31 changed files with 282 additions and 124 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -2,6 +2,7 @@
members = [
"otf",
"pdfrs",
"pdfrs-afm",
"pdfrs-macros",
"serde_pdf",
]
Expand Down
48 changes: 48 additions & 0 deletions pdfrs-afm/Cargo.toml
@@ -0,0 +1,48 @@
[package]
name = "pdfrs-afm"
version = "0.1.0"
authors = ["Markus Ast <m@rkusa.st>"]
edition = "2018"

[dependencies]
lazy_static = "1.4"

[build-dependencies]
regex = "1.1"
itertools = "0.9"
Inflector = "0.11"

[features]
default = ["helvetica"]

all_fonts = [
"courier_bold",
"courier_bold_oblique",
"courier_oblique",
"courier",
"helvetica_bold",
"helvetica_bold_oblique",
"helvetica_oblique",
"helvetica",
"symbol",
"times_bold",
"times_bold_italic",
"times_italic",
"times_roman",
"zapf_dingbats",
]

courier_bold = []
courier_bold_oblique = []
courier_oblique = []
courier = []
helvetica_bold = []
helvetica_bold_oblique = []
helvetica_oblique = []
helvetica = []
symbol = []
times_bold = []
times_bold_italic = []
times_italic = []
times_roman = []
zapf_dingbats = []
2 changes: 1 addition & 1 deletion pdfrs/build.rs → pdfrs-afm/build.rs
Expand Up @@ -69,7 +69,7 @@ fn main() -> io::Result<()> {
include_str!("./fonts/Helvetica-Oblique.afm"),
out_dir.join("helvetica_oblique.rs"),
)?;
#[cfg(feature = "helvetica")]
#[cfg(any(feature = "helvetica", test))]
build_font(
&name_to_code,
"HELVETICA",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
61 changes: 61 additions & 0 deletions pdfrs-afm/src/lib.rs
@@ -0,0 +1,61 @@
//! This module contains built-in PDF (AFM) fonts. When using those fonts, it is not necessary
//! to embed a font into the document, since each PDF reader provides them. However, compared to
//! embedding fonts, those AFM fonts only have a limited set of available characters.
//!
//! Each AFM font needs to be enabled via a Cargo feature. Only `HELVETICA` is enabled as part of
//! the default feature set. The following features for enabling AFM fonts are available:
//! `courier_bold`, `courier_bold_oblique`, `courier_oblique`, `courier`, `helvetica_bold`,
//! `helvetica_bold_oblique`, `helvetica_oblique`, `helvetica`, `symbol`, `times_bold`,
//! `times_bold_italic`, `times_italic`, `times_roman`, `zapf_dingbats`.

#[macro_use]
extern crate lazy_static;

use std::collections::HashMap;

#[derive(Default)]
pub struct AfmFont {
pub cap_height: i32,
pub x_height: i32,
pub ascender: i32,
pub descender: i32,
pub italic_angle: f32,
pub underline_position: i32,
pub underline_thickness: i32,
pub font_bbox: (i32, i32, i32, i32),
pub font_name: &'static str,
pub full_name: &'static str,
pub family_name: &'static str,
pub character_set: &'static str,
pub glyph_widths: HashMap<u8, u32>,
pub kerning: HashMap<(u32, u32), i32>,
}

#[cfg(feature = "courier_bold")]
include!(concat!(env!("OUT_DIR"), "/courier_bold.rs"));
#[cfg(feature = "courier_bold_oblique")]
include!(concat!(env!("OUT_DIR"), "/courier_bold_oblique.rs"));
#[cfg(feature = "courier_oblique")]
include!(concat!(env!("OUT_DIR"), "/courier_oblique.rs"));
#[cfg(feature = "courier")]
include!(concat!(env!("OUT_DIR"), "/courier.rs"));
#[cfg(feature = "helvetica_bold")]
include!(concat!(env!("OUT_DIR"), "/helvetica_bold.rs"));
#[cfg(feature = "helvetica_bold_oblique")]
include!(concat!(env!("OUT_DIR"), "/helvetica_bold_oblique.rs"));
#[cfg(feature = "helvetica_oblique")]
include!(concat!(env!("OUT_DIR"), "/helvetica_oblique.rs"));
#[cfg(feature = "helvetica")]
include!(concat!(env!("OUT_DIR"), "/helvetica.rs"));
#[cfg(feature = "symbol")]
include!(concat!(env!("OUT_DIR"), "/symbol.rs"));
#[cfg(feature = "times_bold")]
include!(concat!(env!("OUT_DIR"), "/times_bold.rs"));
#[cfg(feature = "times_bold_italic")]
include!(concat!(env!("OUT_DIR"), "/times_bold_italic.rs"));
#[cfg(feature = "times_italic")]
include!(concat!(env!("OUT_DIR"), "/times_italic.rs"));
#[cfg(feature = "times_roman")]
include!(concat!(env!("OUT_DIR"), "/times_roman.rs"));
#[cfg(feature = "zapf_dingbats")]
include!(concat!(env!("OUT_DIR"), "/zapf_dingbats.rs"));
36 changes: 17 additions & 19 deletions pdfrs/Cargo.toml
Expand Up @@ -8,20 +8,17 @@ edition = "2018"
async-std = "1.6"
chrono = { version = "0.4", features = ["serde"] }
lazy_static = "1.4"
pdfrs-afm = { path = "../pdfrs-afm", default-features = false, optional = true }
pin-project = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_pdf = { path = "../serde_pdf" }
thiserror = "1.0"
unicode-linebreak = "0.1"
uuid = { version = "0.8", features = ["v4"] }

[build-dependencies]
regex = "1.1"
itertools = "0.9"
Inflector = "0.11"

[dev-dependencies]
async-std = { version = "1.6", features = ["attributes"] }
pdfrs-afm = { path = "../pdfrs-afm" }
pdfrs-macros = { path = "../pdfrs-macros" }
pretty_assertions = "0.6"

Expand All @@ -45,17 +42,18 @@ all_fonts = [
"zapf_dingbats",
]

courier_bold = []
courier_bold_oblique = []
courier_oblique = []
courier = []
helvetica_bold = []
helvetica_bold_oblique = []
helvetica_oblique = []
helvetica = []
symbol = []
times_bold = []
times_bold_italic = []
times_italic = []
times_roman = []
zapf_dingbats = []
afm = []
courier_bold = ["afm", "pdfrs-afm", "pdfrs-afm/courier_bold"]
courier_bold_oblique = ["afm", "pdfrs-afm", "pdfrs-afm/courier_bold_oblique"]
courier_oblique = ["afm", "pdfrs-afm", "pdfrs-afm/courier_oblique"]
courier = ["afm", "pdfrs-afm", "pdfrs-afm/courier"]
helvetica_bold = ["afm", "pdfrs-afm", "pdfrs-afm/helvetica_bold"]
helvetica_bold_oblique = ["afm", "pdfrs-afm", "pdfrs-afm/helvetica_bold_oblique"]
helvetica_oblique = ["afm", "pdfrs-afm", "pdfrs-afm/helvetica_oblique"]
helvetica = ["afm", "pdfrs-afm", "pdfrs-afm/helvetica"]
symbol = ["afm", "pdfrs-afm", "pdfrs-afm/symbol"]
times_bold = ["afm", "pdfrs-afm", "pdfrs-afm/times_bold"]
times_bold_italic = ["afm", "pdfrs-afm", "pdfrs-afm/times_bold_italic"]
times_italic = ["afm", "pdfrs-afm", "pdfrs-afm/times_italic"]
times_roman = ["afm", "pdfrs-afm", "pdfrs-afm/times_roman"]
zapf_dingbats = ["afm", "pdfrs-afm", "pdfrs-afm/zapf_dingbats"]
4 changes: 2 additions & 2 deletions pdfrs/src/document.rs
Expand Up @@ -182,7 +182,7 @@ where
Ok(())
}

pub async fn text(&mut self, text: &str, font: &'a dyn Font) -> Result<(), Error> {
pub async fn text(&mut self, text: &str, font: &'a Font) -> Result<(), Error> {
if !self.fonts.contains_key(font.base_name()) {
if let Some(content_ref) = self.out.end_stream().await? {
self.page_state.contents.push(content_ref);
Expand All @@ -209,7 +209,7 @@ where

match &mut self.out {
Writer::Stream(ref mut s) => {
crate::text::write_text(s, text, font_entry.id).await?;
crate::text::write_text(s, text, font_entry.id, font).await?;
}
Writer::Doc(_) | Writer::Null => {
// FIXME: return error instead, or ignore and do nothing
Expand Down
117 changes: 77 additions & 40 deletions pdfrs/src/fonts/afm.rs
@@ -1,66 +1,103 @@
// TODO: remove once AFM fonts are implemented
#![allow(unused)]
use std::io;

use std::collections::HashMap;
use std::io::{self, Write};
use crate::fonts::font::{Font, FontEncoding, FontObject, FontType, FontVariant};
use serde_pdf::PdfStr;

use super::font::{FontEncoding, FontObject, FontType};
use super::Font;
pub struct AfmFont(&'static pdfrs_afm::AfmFont);

#[derive(Default)]
pub struct AfmFont {
pub(crate) cap_height: i32,
pub(crate) x_height: i32,
pub(crate) ascender: i32,
pub(crate) descender: i32,
pub(crate) italic_angle: f32,
pub(crate) underline_position: i32,
pub(crate) underline_thickness: i32,
pub(crate) font_bbox: (i32, i32, i32, i32),
pub(crate) font_name: &'static str,
pub(crate) full_name: &'static str,
pub(crate) family_name: &'static str,
pub(crate) character_set: &'static str,
pub(crate) glyph_widths: HashMap<u8, u32>,
pub(crate) kerning: HashMap<(u32, u32), i32>,
#[cfg(feature = "courier_bold")]
lazy_static! {
pub static ref COURIER_BOLD: Font = Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::COURIER_BOLD)));
}
#[cfg(feature = "courier_bold_oblique")]
lazy_static! {
pub static ref COURIER_BOLD_OBLIQUE: Font =
Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::COURIER_BOLD_OBLIQUE)));
}
#[cfg(feature = "courier_oblique")]
lazy_static! {
pub static ref COURIER_OBLIQUE: Font =
Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::COURIER_OBLIQUE)));
}
#[cfg(feature = "courier")]
lazy_static! {
pub static ref COURIER: Font = Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::COURIER)));
}
#[cfg(feature = "helvetica_bold")]
lazy_static! {
pub static ref HELVETICA_BOLD: Font =
Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::HELVETICA_BOLD)));
}
#[cfg(feature = "helvetica_bold_oblique")]
lazy_static! {
pub static ref HELVETICA_BOLD_OBLIQUE: Font = Font(FontVariant::Afm(AfmFont(
&*pdfrs_afm::HELVETICA_BOLD_OBLIQUE
)));
}
#[cfg(feature = "helvetica_oblique")]
lazy_static! {
pub static ref HELVETICA_OBLIQUE: Font =
Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::HELVETICA_OBLIQUE)));
}
#[cfg(feature = "helvetica")]
lazy_static! {
pub static ref HELVETICA: Font = Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::HELVETICA)));
}
#[cfg(feature = "symbol")]
lazy_static! {
pub static ref SYMBOL: Font = Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::SYMBOL)));
}
#[cfg(feature = "times_bold")]
lazy_static! {
pub static ref TIMES_BOLD: Font = Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::TIMES_BOLD)));
}
#[cfg(feature = "times_bold_italic")]
lazy_static! {
pub static ref TIMES_BOLD_ITALIC: Font =
Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::TIMES_BOLD_ITALIC)));
}
#[cfg(feature = "times_italic")]
lazy_static! {
pub static ref TIMES_ITALIC: Font = Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::TIMES_ITALIC)));
}
#[cfg(feature = "times_roman")]
lazy_static! {
pub static ref TIMES_ROMAN: Font = Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::TIMES_ROMAN)));
}
#[cfg(feature = "zapf_dingbats")]
lazy_static! {
pub static ref ZAPF_DINGBATS: Font =
Font(FontVariant::Afm(AfmFont(&*pdfrs_afm::ZAPF_DINGBATS)));
}

impl Font for AfmFont {
fn base_name(&self) -> &str {
self.font_name
impl AfmFont {
pub fn base_name(&self) -> &str {
self.0.font_name
}

fn object(&self) -> FontObject<'_> {
pub fn object(&self) -> FontObject<'_> {
FontObject {
subtype: FontType::Type1,
base_font: self.base_name(),
encoding: FontEncoding::WinAnsiEncoding,
}
}

fn kerning(&self, lhs: char, rhs: char) -> Option<i32> {
self.kerning.get(&(lhs as u32, rhs as u32)).cloned()
pub fn kerning(&self, lhs: char, rhs: char) -> Option<i32> {
self.0.kerning.get(&(lhs as u32, rhs as u32)).cloned()
}

fn encode(&self, text: &str, buf: &mut Vec<u8>) -> Result<(), io::Error> {
pub fn encode(&self, text: &str, buf: &mut Vec<u8>) -> Result<(), io::Error> {
buf.clear();
buf.push(b'(');
for c in text.chars() {
match c {
'\\' => buf.extend_from_slice(b"\\\\"),
'(' => buf.extend_from_slice(b"\\("),
')' => buf.extend_from_slice(b"\\)"),
c => buf.push(c as u8),
}
}
buf.push(b')');
buf.extend_from_slice(PdfStr::Literal(text).to_string().as_bytes());
Ok(())
}
}

#[cfg(test)]
mod test {
use crate::fonts::{Font, HELVETICA};
use crate::fonts::afm::HELVETICA;
use crate::fonts::Font;

#[test]
fn test_encode_basic() {
Expand Down
45 changes: 40 additions & 5 deletions pdfrs/src/fonts/font.rs
Expand Up @@ -2,11 +2,46 @@ use std::io;

use serde::Serialize;

pub trait Font {
fn base_name(&self) -> &str;
fn object(&self) -> FontObject<'_>;
fn kerning(&self, lhs: char, rhs: char) -> Option<i32>;
fn encode(&self, text: &str, buf: &mut Vec<u8>) -> Result<(), io::Error>;
pub struct Font(pub(super) FontVariant);

pub(super) enum FontVariant {
#[cfg(feature = "afm")]
Afm(super::afm::AfmFont),
OpenType,
}

impl Font {
pub fn base_name(&self) -> &str {
match &self.0 {
#[cfg(feature = "afm")]
FontVariant::Afm(afm) => afm.base_name(),
FontVariant::OpenType => unimplemented!(),
}
}

pub fn object(&self) -> FontObject<'_> {
match &self.0 {
#[cfg(feature = "afm")]
FontVariant::Afm(afm) => afm.object(),
FontVariant::OpenType => unimplemented!(),
}
}

pub fn kerning(&self, lhs: char, rhs: char) -> Option<i32> {
match &self.0 {
#[cfg(feature = "afm")]
FontVariant::Afm(afm) => afm.kerning(lhs, rhs),
FontVariant::OpenType => unimplemented!(),
}
}

pub fn encode(&self, text: &str, buf: &mut Vec<u8>) -> Result<(), io::Error> {
match &self.0 {
#[cfg(feature = "afm")]
FontVariant::Afm(afm) => afm.encode(text, buf),
FontVariant::OpenType => unimplemented!(),
}
}
}

#[derive(Serialize)]
Expand Down

0 comments on commit 4e7405e

Please sign in to comment.