Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better font management for pdf #95

Merged
merged 8 commits into from
Mar 26, 2024
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 99 additions & 107 deletions dessin-pdf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
use dessin::font::FontRef;
use dessin::{
export::{Export, Exporter},
font::FontGroup,
font::{get, Font, FontGroup, FontHolder},
prelude::*,
};
use nalgebra::Translation2;
use printpdf::{
BuiltinFont, IndirectFontRef, Line, Mm, PdfDocument, PdfDocumentReference, PdfLayerReference,
Point,
};
use std::{collections::HashMap, fmt};
use std::cell::RefCell;
use std::rc::Rc;
use std::{
collections::{HashMap, HashSet},
fmt,
};

#[derive(Debug)]
pub enum PDFError {
PrintPDF(printpdf::Error),
WriteError(fmt::Error),
CurveHasNoStartingPoint(Curve),
UnknownBuiltinFont(String),
OrphelinLayer,
}
impl From<fmt::Error> for PDFError {
fn from(e: fmt::Error) -> Self {
Expand All @@ -28,31 +35,42 @@ impl From<printpdf::Error> for PDFError {
}
}

type PDFFontHolder = HashMap<String, FontGroup<IndirectFontRef>>;
type PDFFontHolder = HashMap<(FontRef, FontWeight), IndirectFontRef>;

#[derive(Default)]
pub struct PDFOptions {
pub size: Option<(f32, f32)>,
pub fonts: PDFFontHolder,
pub used_font: PDFFontHolder,
}

pub struct PDFExporter {
pub struct PDFExporter<'a> {
layer: PdfLayerReference,
fonts: PDFFontHolder,
doc: &'a PdfDocumentReference,
used_font: PDFFontHolder,
}
impl PDFExporter {
pub fn new(layer: PdfLayerReference, fonts: PDFFontHolder) -> Self {
PDFExporter { layer, fonts }
impl<'a> PDFExporter<'a> {
pub fn new(
layer: PdfLayerReference,
doc: &'a PdfDocumentReference,
used_font: PDFFontHolder,
) -> Self {
PDFExporter {
layer,
doc,
used_font,
}
}
pub fn new_with_default_font(layer: PdfLayerReference) -> Self {
pub fn new_with_default_font(layer: PdfLayerReference, doc: &'a PdfDocumentReference) -> Self {
let stock: PDFFontHolder = HashMap::default();
PDFExporter {
layer,
fonts: PDFFontHolder::default(),
doc,
used_font: stock,
}
}
}

impl Exporter for PDFExporter {
impl Exporter for PDFExporter<'_> {
type Error = PDFError;
const CAN_EXPORT_ELLIPSE: bool = false;

Expand Down Expand Up @@ -223,43 +241,78 @@ impl Exporter for PDFExporter {
Ok(())
}

fn export_text(&mut self, text: TextPosition) -> Result<(), Self::Error> {
let font = text
.font
.as_ref()
.map(|f| f.font_family())
.unwrap_or("default");
let font = self
.fonts
.get(font)
.and_then(|font| match text.font_weight {
FontWeight::Regular => Some(font.regular.clone()),
FontWeight::Bold => font.bold.clone(),
FontWeight::Italic => font.italic.clone(),
FontWeight::BoldItalic => font.bold_italic.clone(),
})
.unwrap();
fn export_text(
&mut self,
TextPosition {
text,
align,
font_weight,
on_curve,
font_size,
reference_start,
direction,
font,
}: TextPosition,
) -> Result<(), Self::Error> {
let font = font.clone().unwrap_or(FontRef::default());

let font_name = font.clone().name(font_weight);

// search if (font_ref, font_weight) is stocked in used_font
if !self.used_font.contains_key(&(font.clone(), font_weight)) {
}
// if it's not, we can insert the font into the PDF
else {
self.used_font.insert(
(font.clone(), font_weight),
match get(font.clone()).get(font_weight) {
dessin::font::Font::OTF(b) | dessin::font::Font::TTF(b) => {
self.doc.add_external_font(b.as_slice())?
}
},
);

let font_group = font::get(font.clone());
let (mime, bytes) = match font_group.get(font_weight) {
dessin::font::Font::OTF(bytes) => ("font/otf", bytes),
dessin::font::Font::TTF(bytes) => ("font/ttf", bytes),
};

self.doc.add_external_font(bytes.as_slice());
}

// transform font into an IndirectFontRef to be used after
let will_survive = get(font);
let font = will_survive.get(font_weight);
let font: IndirectFontRef = match font {
// dessin::font::Font::ByName(n) => doc.add_builtin_font(find_builtin_font(&n)?)?,
dessin::font::Font::OTF(b) | dessin::font::Font::TTF(b) => {
self.doc.add_external_font(b.as_slice())?
}
};

self.layer.begin_text_section();
self.layer.set_font(&font, text.font_size);
self.layer.set_font(&font, font_size);
// if let Some(te) = text.on_curve {
// self.layer.add_polygon()
// todo!()
// }
let rotation = text.direction.y.atan2(text.direction.x).to_degrees();
let rotation = direction.y.atan2(direction.x).to_degrees();
self.layer
.set_text_rendering_mode(printpdf::TextRenderingMode::Fill);
self.layer
.set_text_matrix(printpdf::TextMatrix::TranslateRotate(
Mm(text.reference_start.x).into_pt(),
Mm(text.reference_start.y).into_pt(),
Mm(reference_start.x).into_pt(),
Mm(reference_start.y).into_pt(),
rotation,
));

// self.layer.set_line_height(text.font_size);
// self.layer.set_word_spacing(3000.0);
// self.layer.set_character_spacing(10.0);
self.layer.write_text(text.text, &font);
self.layer.write_text(text, &font);
self.layer.end_text_section();

Ok(())
}
}
Expand All @@ -269,10 +322,15 @@ pub trait ToPDF {
&self,
layer: PdfLayerReference,
options: PDFOptions,
doc: &PdfDocumentReference,
) -> Result<(), PDFError>;
#[inline]
fn write_to_pdf(&self, layer: PdfLayerReference) -> Result<(), PDFError> {
self.write_to_pdf_with_options(layer, PDFOptions::default())
fn write_to_pdf(
&self,
layer: PdfLayerReference,
doc: &PdfDocumentReference,
) -> Result<(), PDFError> {
self.write_to_pdf_with_options(layer, PDFOptions::default(), doc)
}

fn to_pdf_with_options(&self, options: PDFOptions) -> Result<PdfDocumentReference, PDFError>;
Expand All @@ -293,14 +351,15 @@ pub trait ToPDF {
impl ToPDF for Shape {
fn write_to_pdf_with_options(
&self,
layer: PdfLayerReference,
layer: PdfLayerReference, //doc ?
options: PDFOptions,
doc: &PdfDocumentReference,
) -> Result<(), PDFError> {
let (width, height) = options.size.unwrap_or_else(|| {
let bb = self.local_bounding_box();
(bb.width(), bb.height())
});
let mut exporter = PDFExporter::new(layer, options.fonts);
let mut exporter = PDFExporter::new(layer, doc, options.used_font);
let translation = Translation2::new(width / 2., height / 2.);
let parent_transform = nalgebra::convert(translation);

Expand All @@ -311,83 +370,16 @@ impl ToPDF for Shape {
&self,
mut options: PDFOptions,
) -> Result<PdfDocumentReference, PDFError> {
// creates the document
let size = options.size.get_or_insert_with(|| {
let bb = self.local_bounding_box();
(bb.width(), bb.height())
});
let (doc, page, layer) = PdfDocument::new("", Mm(size.0), Mm(size.1), "Layer 1");
let layer = doc.get_page(page).get_layer(layer);
let default_regular = doc.add_builtin_font(BuiltinFont::Helvetica).unwrap();
let default_bold = doc.add_builtin_font(BuiltinFont::HelveticaBold).unwrap();
let default_italic = doc.add_builtin_font(BuiltinFont::HelveticaOblique).unwrap();
let default_bold_italic = doc
.add_builtin_font(BuiltinFont::HelveticaBoldOblique)
.unwrap();
options.fonts.insert(
"default".to_string(),
dessin::font::FontGroup {
regular: default_regular,
bold: Some(default_bold),
bold_italic: Some(default_bold_italic),
italic: Some(default_italic),
},
);

for (
key,
dessin::font::FontGroup {
regular,
bold,
italic,
bold_italic,
},
) in dessin::font::fonts()
{
let regular = match regular {
// dessin::font::Font::ByName(n) => doc.add_builtin_font(find_builtin_font(&n)?)?,
dessin::font::Font::OTF(b) | dessin::font::Font::TTF(b) => {
doc.add_external_font(b.as_slice())?
}
};

let bold = match bold {
// Some(dessin::font::Font::ByName(n)) => {
// Some(doc.add_builtin_font(find_builtin_font(&n)?)?)
// }
Some(dessin::font::Font::OTF(b) | dessin::font::Font::TTF(b)) => {
Some(doc.add_external_font(b.as_slice())?)
}
None => None,
};

let italic = match italic {
// Some(dessin::font::Font::ByName(n)) => {
// Some(doc.add_builtin_font(find_builtin_font(&n)?)?)
// }
Some(dessin::font::Font::OTF(b) | dessin::font::Font::TTF(b)) => {
Some(doc.add_external_font(b.as_slice())?)
}
None => None,
};
let layer = doc.get_page(page).get_layer(layer);

let bold_italic = match bold_italic {
// Some(dessin::font::Font::ByName(n)) => {
// Some(doc.add_builtin_font(find_builtin_font(&n)?)?)
// }
Some(dessin::font::Font::OTF(b) | dessin::font::Font::TTF(b)) => {
Some(doc.add_external_font(b.as_slice())?)
}
None => None,
};
let fonts_group = dessin::font::FontGroup {
regular,
bold,
bold_italic,
italic,
};
options.fonts.insert(key, fonts_group);
}
self.write_to_pdf_with_options(layer, options)?;
self.write_to_pdf_with_options(layer, options, &doc)?;

Ok(doc)
}
Expand Down
Loading