mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Streamline font handling a bit 📜
This commit is contained in:
parent
ee11f87175
commit
7143e10afc
@ -3,10 +3,9 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
use fontdock::fs::{FsIndex, FsProvider};
|
use fontdock::fs::{FsIndex, FsProvider};
|
||||||
use fontdock::FontLoader;
|
|
||||||
|
|
||||||
use typstc::font::DynProvider;
|
use typstc::font::FontLoader;
|
||||||
use typstc::syntax::parsing::parse;
|
use typstc::parse::parse;
|
||||||
use typstc::Typesetter;
|
use typstc::Typesetter;
|
||||||
|
|
||||||
const FONT_DIR: &str = "fonts";
|
const FONT_DIR: &str = "fonts";
|
||||||
@ -25,10 +24,9 @@ fn typesetting_benchmark(c: &mut Criterion) {
|
|||||||
let mut index = FsIndex::new();
|
let mut index = FsIndex::new();
|
||||||
index.search_dir(FONT_DIR);
|
index.search_dir(FONT_DIR);
|
||||||
|
|
||||||
let (descriptors, files) = index.clone().into_vecs();
|
let (descriptors, files) = index.into_vecs();
|
||||||
let provider = FsProvider::new(files.clone());
|
let provider = FsProvider::new(files);
|
||||||
let dynamic = Box::new(provider) as Box<DynProvider>;
|
let loader = FontLoader::new(Box::new(provider), descriptors);
|
||||||
let loader = FontLoader::new(dynamic, descriptors);
|
|
||||||
let loader = Rc::new(RefCell::new(loader));
|
let loader = Rc::new(RefCell::new(loader));
|
||||||
let typesetter = Typesetter::new(loader.clone());
|
let typesetter = Typesetter::new(loader.clone());
|
||||||
|
|
||||||
|
10
main/main.rs
10
main/main.rs
@ -5,11 +5,10 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use fontdock::fs::{FsIndex, FsProvider};
|
use fontdock::fs::{FsIndex, FsProvider};
|
||||||
use fontdock::FontLoader;
|
|
||||||
use futures_executor::block_on;
|
use futures_executor::block_on;
|
||||||
|
|
||||||
use typstc::export::pdf;
|
use typstc::export::pdf;
|
||||||
use typstc::font::DynProvider;
|
use typstc::font::FontLoader;
|
||||||
use typstc::Typesetter;
|
use typstc::Typesetter;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -37,9 +36,8 @@ fn main() {
|
|||||||
index.search_os();
|
index.search_os();
|
||||||
|
|
||||||
let (descriptors, files) = index.into_vecs();
|
let (descriptors, files) = index.into_vecs();
|
||||||
let provider = FsProvider::new(files.clone());
|
let provider = FsProvider::new(files);
|
||||||
let dynamic = Box::new(provider) as Box<DynProvider>;
|
let loader = FontLoader::new(Box::new(provider), descriptors);
|
||||||
let loader = FontLoader::new(dynamic, descriptors);
|
|
||||||
let loader = Rc::new(RefCell::new(loader));
|
let loader = Rc::new(RefCell::new(loader));
|
||||||
|
|
||||||
let typesetter = Typesetter::new(loader.clone());
|
let typesetter = Typesetter::new(loader.clone());
|
||||||
@ -62,8 +60,8 @@ fn main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loader = loader.borrow();
|
||||||
let file = File::create(&dest_path).expect("failed to create output file");
|
let file = File::create(&dest_path).expect("failed to create output file");
|
||||||
|
|
||||||
let writer = BufWriter::new(file);
|
let writer = BufWriter::new(file);
|
||||||
pdf::export(&layouts, &loader, writer).expect("failed to export pdf");
|
pdf::export(&layouts, &loader, writer).expect("failed to export pdf");
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,10 @@ use tide::font::{
|
|||||||
use tide::{PdfWriter, Rect, Ref, Trailer, Version};
|
use tide::{PdfWriter, Rect, Ref, Trailer, Version};
|
||||||
use ttf_parser::{name_id, GlyphId};
|
use ttf_parser::{name_id, GlyphId};
|
||||||
|
|
||||||
|
use crate::font::FontLoader;
|
||||||
use crate::layout::elements::LayoutElement;
|
use crate::layout::elements::LayoutElement;
|
||||||
use crate::layout::BoxLayout;
|
use crate::layout::BoxLayout;
|
||||||
use crate::length::Length;
|
use crate::length::Length;
|
||||||
use crate::SharedFontLoader;
|
|
||||||
|
|
||||||
/// Export a list of layouts into a _PDF_ document.
|
/// Export a list of layouts into a _PDF_ document.
|
||||||
///
|
///
|
||||||
@ -28,7 +28,7 @@ use crate::SharedFontLoader;
|
|||||||
/// bytes written.
|
/// bytes written.
|
||||||
pub fn export<W: Write>(
|
pub fn export<W: Write>(
|
||||||
layout: &[BoxLayout],
|
layout: &[BoxLayout],
|
||||||
loader: &SharedFontLoader,
|
loader: &FontLoader,
|
||||||
target: W,
|
target: W,
|
||||||
) -> io::Result<usize> {
|
) -> io::Result<usize> {
|
||||||
PdfExporter::new(layout, loader, target)?.write()
|
PdfExporter::new(layout, loader, target)?.write()
|
||||||
@ -37,7 +37,7 @@ pub fn export<W: Write>(
|
|||||||
struct PdfExporter<'a, W: Write> {
|
struct PdfExporter<'a, W: Write> {
|
||||||
writer: PdfWriter<W>,
|
writer: PdfWriter<W>,
|
||||||
layouts: &'a [BoxLayout],
|
layouts: &'a [BoxLayout],
|
||||||
loader: &'a SharedFontLoader,
|
loader: &'a FontLoader,
|
||||||
/// We need to know exactly which indirect reference id will be used for
|
/// We need to know exactly which indirect reference id will be used for
|
||||||
/// which objects up-front to correctly declare the document catalogue, page
|
/// which objects up-front to correctly declare the document catalogue, page
|
||||||
/// tree and so on. These offsets are computed in the beginning and stored
|
/// tree and so on. These offsets are computed in the beginning and stored
|
||||||
@ -61,7 +61,7 @@ const NUM_OBJECTS_PER_FONT: u32 = 5;
|
|||||||
impl<'a, W: Write> PdfExporter<'a, W> {
|
impl<'a, W: Write> PdfExporter<'a, W> {
|
||||||
fn new(
|
fn new(
|
||||||
layouts: &'a [BoxLayout],
|
layouts: &'a [BoxLayout],
|
||||||
loader: &'a SharedFontLoader,
|
loader: &'a FontLoader,
|
||||||
target: W,
|
target: W,
|
||||||
) -> io::Result<Self> {
|
) -> io::Result<Self> {
|
||||||
let (to_pdf, to_fontdock) = remap_fonts(layouts);
|
let (to_pdf, to_fontdock) = remap_fonts(layouts);
|
||||||
@ -168,8 +168,8 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
let mut id = self.offsets.fonts.0;
|
let mut id = self.offsets.fonts.0;
|
||||||
|
|
||||||
for &face_id in &self.to_layout {
|
for &face_id in &self.to_layout {
|
||||||
let loader = self.loader.borrow();
|
let owned_face = self.loader.get_loaded(face_id);
|
||||||
let face = loader.get_loaded(face_id);
|
let face = owned_face.get();
|
||||||
|
|
||||||
let name = face
|
let name = face
|
||||||
.names()
|
.names()
|
||||||
@ -269,7 +269,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
|
|||||||
.write_obj(id + 3, &CMap::new("Custom", system_info, mapping))?;
|
.write_obj(id + 3, &CMap::new("Custom", system_info, mapping))?;
|
||||||
|
|
||||||
// Write the face's bytes.
|
// Write the face's bytes.
|
||||||
self.writer.write_obj(id + 4, &FontStream::new(face.data()))?;
|
self.writer.write_obj(id + 4, &FontStream::new(owned_face.data()))?;
|
||||||
|
|
||||||
id += NUM_OBJECTS_PER_FONT;
|
id += NUM_OBJECTS_PER_FONT;
|
||||||
}
|
}
|
||||||
|
41
src/font.rs
41
src/font.rs
@ -1,25 +1,34 @@
|
|||||||
//! Font handling.
|
//! Font handling.
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use fontdock::{ContainsChar, FaceFromVec, FontLoader, FontProvider};
|
use fontdock::{ContainsChar, FaceFromVec, FontProvider};
|
||||||
use ttf_parser::Face;
|
use ttf_parser::Face;
|
||||||
|
|
||||||
/// A referenced-count shared font loader backed by a dynamic provider.
|
/// A reference-counted shared font loader backed by a dynamic font provider.
|
||||||
pub type SharedFontLoader = Rc<RefCell<FontLoader<Box<DynProvider>>>>;
|
pub type SharedFontLoader = Rc<RefCell<FontLoader>>;
|
||||||
|
|
||||||
/// The dynamic font provider type backing the font loader.
|
/// A font loader backed by a dynamic provider.
|
||||||
|
pub type FontLoader = fontdock::FontLoader<Box<DynProvider>>;
|
||||||
|
|
||||||
|
/// The dynamic font provider backing the font loader.
|
||||||
pub type DynProvider = dyn FontProvider<Face = OwnedFace>;
|
pub type DynProvider = dyn FontProvider<Face = OwnedFace>;
|
||||||
|
|
||||||
/// An owned font face.
|
/// An owned font face.
|
||||||
pub struct OwnedFace {
|
pub struct OwnedFace {
|
||||||
data: Vec<u8>,
|
data: Box<[u8]>,
|
||||||
face: Face<'static>,
|
face: Face<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OwnedFace {
|
impl OwnedFace {
|
||||||
|
/// Get a reference to the underlying face.
|
||||||
|
pub fn get<'a>(&'a self) -> &'a Face<'a> {
|
||||||
|
// We can't implement Deref because that would leak the internal 'static
|
||||||
|
// lifetime.
|
||||||
|
&self.face
|
||||||
|
}
|
||||||
|
|
||||||
/// The raw face data.
|
/// The raw face data.
|
||||||
pub fn data(&self) -> &[u8] {
|
pub fn data(&self) -> &[u8] {
|
||||||
&self.data
|
&self.data
|
||||||
@ -28,13 +37,15 @@ impl OwnedFace {
|
|||||||
|
|
||||||
impl FaceFromVec for OwnedFace {
|
impl FaceFromVec for OwnedFace {
|
||||||
fn from_vec(vec: Vec<u8>, i: u32) -> Option<Self> {
|
fn from_vec(vec: Vec<u8>, i: u32) -> Option<Self> {
|
||||||
// The vec's location is stable in memory since we don't touch it and
|
let data = vec.into_boxed_slice();
|
||||||
// it can't be touched from outside this type.
|
|
||||||
|
// SAFETY: The slices's location is stable in memory since we don't
|
||||||
|
// touch it and it can't be touched from outside this type.
|
||||||
let slice: &'static [u8] =
|
let slice: &'static [u8] =
|
||||||
unsafe { std::slice::from_raw_parts(vec.as_ptr(), vec.len()) };
|
unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) };
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
data: vec,
|
data,
|
||||||
face: Face::from_slice(slice, i).ok()?,
|
face: Face::from_slice(slice, i).ok()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -42,14 +53,6 @@ impl FaceFromVec for OwnedFace {
|
|||||||
|
|
||||||
impl ContainsChar for OwnedFace {
|
impl ContainsChar for OwnedFace {
|
||||||
fn contains_char(&self, c: char) -> bool {
|
fn contains_char(&self, c: char) -> bool {
|
||||||
self.glyph_index(c).is_some()
|
self.get().glyph_index(c).is_some()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for OwnedFace {
|
|
||||||
type Target = Face<'static>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.face
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use ttf_parser::GlyphId;
|
|||||||
|
|
||||||
use super::elements::{LayoutElement, Shaped};
|
use super::elements::{LayoutElement, Shaped};
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::font::SharedFontLoader;
|
use crate::font::FontLoader;
|
||||||
use crate::geom::Size;
|
use crate::geom::Size;
|
||||||
use crate::style::TextStyle;
|
use crate::style::TextStyle;
|
||||||
|
|
||||||
@ -30,11 +30,11 @@ struct TextLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The context for text layouting.
|
/// The context for text layouting.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct TextContext<'a> {
|
pub struct TextContext<'a> {
|
||||||
/// The font loader to retrieve fonts from when typesetting text with
|
/// The font loader to retrieve fonts from when typesetting text with
|
||||||
/// `layout_text`.
|
/// `layout_text`.
|
||||||
pub loader: &'a SharedFontLoader,
|
pub loader: &'a mut FontLoader,
|
||||||
/// The style for text: Font selection with classes, weights and variants,
|
/// The style for text: Font selection with classes, weights and variants,
|
||||||
/// font sizes, spacing and so on.
|
/// font sizes, spacing and so on.
|
||||||
pub style: &'a TextStyle,
|
pub style: &'a TextStyle,
|
||||||
@ -50,12 +50,12 @@ pub struct TextContext<'a> {
|
|||||||
impl<'a> TextLayouter<'a> {
|
impl<'a> TextLayouter<'a> {
|
||||||
fn new(text: &'a str, ctx: TextContext<'a>) -> Self {
|
fn new(text: &'a str, ctx: TextContext<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
|
||||||
text,
|
text,
|
||||||
shaped: Shaped::new(FaceId::MAX, ctx.style.font_size()),
|
shaped: Shaped::new(FaceId::MAX, ctx.style.font_size()),
|
||||||
elements: LayoutElements::new(),
|
elements: LayoutElements::new(),
|
||||||
start: 0.0,
|
start: 0.0,
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
|
ctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,6 @@ impl<'a> TextLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn select_font(&mut self, c: char) -> Option<(FaceId, GlyphId, f64)> {
|
async fn select_font(&mut self, c: char) -> Option<(FaceId, GlyphId, f64)> {
|
||||||
let mut loader = self.ctx.loader.borrow_mut();
|
|
||||||
let mut variant = self.ctx.style.variant;
|
let mut variant = self.ctx.style.variant;
|
||||||
|
|
||||||
if self.ctx.style.bolder {
|
if self.ctx.style.bolder {
|
||||||
@ -137,7 +136,9 @@ impl<'a> TextLayouter<'a> {
|
|||||||
c,
|
c,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((id, face)) = loader.query(query).await {
|
if let Some((id, owned_face)) = self.ctx.loader.query(query).await {
|
||||||
|
let face = owned_face.get();
|
||||||
|
|
||||||
// Determine the width of the char.
|
// Determine the width of the char.
|
||||||
let units_per_em = face.units_per_em().unwrap_or(1000) as f64;
|
let units_per_em = face.units_per_em().unwrap_or(1000) as f64;
|
||||||
let ratio = 1.0 / units_per_em;
|
let ratio = 1.0 / units_per_em;
|
||||||
|
@ -107,7 +107,7 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
async fn layout_text(&mut self, text: &str) {
|
async fn layout_text(&mut self, text: &str) {
|
||||||
self.layouter.add(
|
self.layouter.add(
|
||||||
layout_text(text, TextContext {
|
layout_text(text, TextContext {
|
||||||
loader: &self.ctx.loader,
|
loader: &mut self.ctx.loader.borrow_mut(),
|
||||||
style: &self.style.text,
|
style: &self.style.text,
|
||||||
dir: self.ctx.axes.primary,
|
dir: self.ctx.axes.primary,
|
||||||
align: self.ctx.align,
|
align: self.ctx.align,
|
||||||
|
@ -7,13 +7,12 @@ use std::path::Path;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use fontdock::fs::{FsIndex, FsProvider};
|
use fontdock::fs::{FsIndex, FsProvider};
|
||||||
use fontdock::FontLoader;
|
|
||||||
use futures_executor::block_on;
|
use futures_executor::block_on;
|
||||||
use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
|
use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
|
||||||
use ttf_parser::OutlineBuilder;
|
use ttf_parser::OutlineBuilder;
|
||||||
|
|
||||||
use typstc::export::pdf;
|
use typstc::export::pdf;
|
||||||
use typstc::font::{DynProvider, SharedFontLoader};
|
use typstc::font::{FontLoader, SharedFontLoader};
|
||||||
use typstc::geom::{Size, Value4};
|
use typstc::geom::{Size, Value4};
|
||||||
use typstc::layout::elements::{LayoutElement, Shaped};
|
use typstc::layout::elements::{LayoutElement, Shaped};
|
||||||
use typstc::layout::MultiLayout;
|
use typstc::layout::MultiLayout;
|
||||||
@ -60,13 +59,12 @@ fn main() {
|
|||||||
let mut index = FsIndex::new();
|
let mut index = FsIndex::new();
|
||||||
index.search_dir(FONT_DIR);
|
index.search_dir(FONT_DIR);
|
||||||
|
|
||||||
let (descriptors, files) = index.clone().into_vecs();
|
let (descriptors, files) = index.into_vecs();
|
||||||
let provider = FsProvider::new(files.clone());
|
let provider = FsProvider::new(files);
|
||||||
let dynamic = Box::new(provider) as Box<DynProvider>;
|
let loader = FontLoader::new(Box::new(provider), descriptors);
|
||||||
let loader = FontLoader::new(dynamic, descriptors);
|
|
||||||
let loader = Rc::new(RefCell::new(loader));
|
let loader = Rc::new(RefCell::new(loader));
|
||||||
let mut typesetter = Typesetter::new(loader.clone());
|
|
||||||
|
|
||||||
|
let mut typesetter = Typesetter::new(loader.clone());
|
||||||
typesetter.set_page_style(PageStyle {
|
typesetter.set_page_style(PageStyle {
|
||||||
class: PaperClass::Custom,
|
class: PaperClass::Custom,
|
||||||
size: Size::with_all(Length::pt(250.0).as_raw()),
|
size: Size::with_all(Length::pt(250.0).as_raw()),
|
||||||
@ -106,6 +104,8 @@ fn test(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loader = loader.borrow();
|
||||||
|
|
||||||
let png_path = format!("{}/{}.png", OUT_DIR, name);
|
let png_path = format!("{}/{}.png", OUT_DIR, name);
|
||||||
render(&layouts, &loader, 3.0).write_png(png_path).unwrap();
|
render(&layouts, &loader, 3.0).write_png(png_path).unwrap();
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ impl TestFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(layouts: &MultiLayout, loader: &SharedFontLoader, scale: f64) -> DrawTarget {
|
fn render(layouts: &MultiLayout, loader: &FontLoader, scale: f64) -> DrawTarget {
|
||||||
let pad = scale * 10.0;
|
let pad = scale * 10.0;
|
||||||
let width = 2.0 * pad
|
let width = 2.0 * pad
|
||||||
+ layouts
|
+ layouts
|
||||||
@ -191,13 +191,12 @@ fn render(layouts: &MultiLayout, loader: &SharedFontLoader, scale: f64) -> DrawT
|
|||||||
|
|
||||||
fn render_shaped(
|
fn render_shaped(
|
||||||
surface: &mut DrawTarget,
|
surface: &mut DrawTarget,
|
||||||
loader: &SharedFontLoader,
|
loader: &FontLoader,
|
||||||
shaped: &Shaped,
|
shaped: &Shaped,
|
||||||
pos: Size,
|
pos: Size,
|
||||||
scale: f64,
|
scale: f64,
|
||||||
) {
|
) {
|
||||||
let loader = loader.borrow();
|
let face = loader.get_loaded(shaped.face).get();
|
||||||
let face = loader.get_loaded(shaped.face);
|
|
||||||
|
|
||||||
for (&glyph, &offset) in shaped.glyphs.iter().zip(&shaped.offsets) {
|
for (&glyph, &offset) in shaped.glyphs.iter().zip(&shaped.offsets) {
|
||||||
let mut builder = WrappedPathBuilder(PathBuilder::new());
|
let mut builder = WrappedPathBuilder(PathBuilder::new());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user