summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/export/pdf.rs47
-rw-r--r--src/font.rs54
-rw-r--r--src/geom/angle.rs3
-rw-r--r--src/geom/em.rs112
-rw-r--r--src/geom/fr.rs2
-rw-r--r--src/geom/length.rs3
-rw-r--r--src/geom/mod.rs5
-rw-r--r--src/geom/path.rs2
-rw-r--r--src/geom/point.rs2
-rw-r--r--src/geom/relative.rs2
-rw-r--r--src/geom/size.rs2
-rw-r--r--src/layout/frame.rs18
-rw-r--r--src/layout/shaping.rs22
13 files changed, 181 insertions, 93 deletions
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index e77ad931..7ff600ff 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -14,8 +14,8 @@ use pdf_writer::{
use ttf_parser::{name_id, GlyphId};
use crate::color::Color;
-use crate::font::{Em, FaceId, FontStore};
-use crate::geom::{self, Length, Size};
+use crate::font::{FaceId, FontStore};
+use crate::geom::{self, Em, Length, Size};
use crate::image::{Image, ImageId, ImageStore};
use crate::layout::{Element, Frame, Geometry, Paint};
use crate::Context;
@@ -140,7 +140,7 @@ impl<'a> PdfExporter<'a> {
// We only write font switching actions when the used face changes. To
// do that, we need to remember the active face.
- let mut face = None;
+ let mut face_id = None;
let mut size = Length::zero();
let mut fill: Option<Paint> = None;
@@ -159,17 +159,50 @@ impl<'a> PdfExporter<'a> {
// Then, also check if we need to issue a font switching
// action.
- if face != Some(shaped.face_id) || shaped.size != size {
- face = Some(shaped.face_id);
+ if face_id != Some(shaped.face_id) || shaped.size != size {
+ face_id = Some(shaped.face_id);
size = shaped.size;
let name = format!("F{}", self.font_map.map(shaped.face_id));
text.font(Name(name.as_bytes()), size.to_pt() as f32);
}
- // TODO: Respect individual glyph offsets.
+ let face = self.fonts.get(shaped.face_id);
+
+ // Position the text.
text.matrix(1.0, 0.0, 0.0, 1.0, x, y);
- text.show(Str(&shaped.encode_glyphs_be()));
+
+ let mut positioned = text.show_positioned();
+ let mut adjustment = Em::zero();
+ let mut encoded = vec![];
+
+ // Write the glyphs with kerning adjustments.
+ for glyph in &shaped.glyphs {
+ adjustment += glyph.x_offset;
+
+ if !adjustment.is_zero() {
+ if !encoded.is_empty() {
+ positioned.show(Str(&encoded));
+ encoded.clear();
+ }
+
+ positioned.adjust(-adjustment.to_pdf());
+ adjustment = Em::zero();
+ }
+
+ encoded.push((glyph.id >> 8) as u8);
+ encoded.push((glyph.id & 0xff) as u8);
+
+ if let Some(advance) = face.advance(glyph.id) {
+ adjustment += glyph.x_advance - advance;
+ }
+
+ adjustment -= glyph.x_offset;
+ }
+
+ if !encoded.is_empty() {
+ positioned.show(Str(&encoded));
+ }
}
Element::Geometry(ref geometry, paint) => {
diff --git a/src/font.rs b/src/font.rs
index dadf6830..690884f4 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -2,15 +2,13 @@
use std::collections::{hash_map::Entry, HashMap};
use std::fmt::{self, Debug, Display, Formatter};
-use std::ops::Add;
use std::path::{Path, PathBuf};
use std::rc::Rc;
-use decorum::N64;
use serde::{Deserialize, Serialize};
-use ttf_parser::name_id;
+use ttf_parser::{name_id, GlyphId};
-use crate::geom::Length;
+use crate::geom::Em;
use crate::loading::{FileHash, Loader};
/// A unique identifier for a loaded font face.
@@ -255,6 +253,13 @@ impl Face {
Em::from_units(units, self.units_per_em)
}
+ /// Look up the horizontal advance width of a glyph.
+ pub fn advance(&self, glyph: u16) -> Option<Em> {
+ self.ttf
+ .glyph_hor_advance(GlyphId(glyph))
+ .map(|units| self.to_em(units))
+ }
+
/// Look up a vertical metric.
pub fn vertical_metric(&self, metric: VerticalFontMetric) -> Em {
match metric {
@@ -267,47 +272,6 @@ impl Face {
}
}
-/// A length in em units.
-///
-/// `1em` is the same as the font size.
-#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub struct Em(N64);
-
-impl Em {
- /// The zero length.
- pub fn zero() -> Self {
- Self(N64::from(0.0))
- }
-
- /// Create an em length.
- pub fn new(em: f64) -> Self {
- Self(N64::from(em))
- }
-
- /// Convert units to an em length at the given units per em.
- pub fn from_units(units: impl Into<f64>, units_per_em: f64) -> Self {
- Self(N64::from(units.into() / units_per_em))
- }
-
- /// The number of em units.
- pub fn get(self) -> f64 {
- self.0.into()
- }
-
- /// Convert to a length at the given font size.
- pub fn to_length(self, font_size: Length) -> Length {
- self.get() * font_size
- }
-}
-
-impl Add for Em {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self(self.0 + other.0)
- }
-}
-
/// Identifies a vertical metric of a font.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum VerticalFontMetric {
diff --git a/src/geom/angle.rs b/src/geom/angle.rs
index cf3e49a0..023b1d98 100644
--- a/src/geom/angle.rs
+++ b/src/geom/angle.rs
@@ -1,6 +1,3 @@
-use decorum::N64;
-use serde::{Deserialize, Serialize};
-
use super::*;
/// An angle.
diff --git a/src/geom/em.rs b/src/geom/em.rs
new file mode 100644
index 00000000..45e5ba60
--- /dev/null
+++ b/src/geom/em.rs
@@ -0,0 +1,112 @@
+use super::*;
+
+/// A length that is relative to the font size.
+///
+/// `1em` is the same as the font size.
+#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Serialize, Deserialize)]
+pub struct Em(N64);
+
+impl Em {
+ /// The zero length.
+ pub fn zero() -> Self {
+ Self(N64::from(0.0))
+ }
+
+ /// The font size.
+ pub fn one() -> Self {
+ Self(N64::from(1.0))
+ }
+
+ /// Create an font-relative length.
+ pub fn new(em: f64) -> Self {
+ Self(N64::from(em))
+ }
+
+ /// Create font units at the given units per em.
+ pub fn from_units(units: impl Into<f64>, units_per_em: f64) -> Self {
+ Self(N64::from(units.into() / units_per_em))
+ }
+
+ /// Convert to a length at the given font size.
+ pub fn to_length(self, font_size: Length) -> Length {
+ self.get() * font_size
+ }
+
+ /// The number of em units.
+ pub fn get(self) -> f64 {
+ self.0.into()
+ }
+
+ /// Whether the length is zero.
+ pub fn is_zero(self) -> bool {
+ self.0 == 0.0
+ }
+}
+
+impl Debug for Em {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ Display::fmt(self, f)
+ }
+}
+
+impl Display for Em {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{}em", self.get())
+ }
+}
+
+impl Neg for Em {
+ type Output = Self;
+
+ fn neg(self) -> Self {
+ Self(-self.0)
+ }
+}
+
+impl Add for Em {
+ type Output = Self;
+
+ fn add(self, other: Self) -> Self {
+ Self(self.0 + other.0)
+ }
+}
+
+sub_impl!(Em - Em -> Em);
+
+impl Mul<f64> for Em {
+ type Output = Self;
+
+ fn mul(self, other: f64) -> Self {
+ Self(self.0 * other)
+ }
+}
+
+impl Mul<Em> for f64 {
+ type Output = Em;
+
+ fn mul(self, other: Em) -> Em {
+ other * self
+ }
+}
+
+impl Div<f64> for Em {
+ type Output = Self;
+
+ fn div(self, other: f64) -> Self {
+ Self(self.0 / other)
+ }
+}
+
+impl Div for Em {
+ type Output = f64;
+
+ fn div(self, other: Self) -> f64 {
+ self.get() / other.get()
+ }
+}
+
+assign_impl!(Em += Em);
+assign_impl!(Em -= Em);
+assign_impl!(Em *= f64);
+assign_impl!(Em /= f64);
diff --git a/src/geom/fr.rs b/src/geom/fr.rs
index eaf539d9..ed0c8329 100644
--- a/src/geom/fr.rs
+++ b/src/geom/fr.rs
@@ -1,5 +1,3 @@
-use decorum::N64;
-
use super::*;
/// A fractional length.
diff --git a/src/geom/length.rs b/src/geom/length.rs
index 3510fa6f..f8484f75 100644
--- a/src/geom/length.rs
+++ b/src/geom/length.rs
@@ -1,6 +1,3 @@
-use decorum::N64;
-use serde::{Deserialize, Serialize};
-
use super::*;
/// An absolute length.
diff --git a/src/geom/mod.rs b/src/geom/mod.rs
index 6f48d7d9..11e082b8 100644
--- a/src/geom/mod.rs
+++ b/src/geom/mod.rs
@@ -5,6 +5,7 @@ mod macros;
mod align;
mod angle;
mod dir;
+mod em;
mod fr;
mod gen;
mod length;
@@ -19,6 +20,7 @@ mod spec;
pub use align::*;
pub use angle::*;
pub use dir::*;
+pub use em::*;
pub use fr::*;
pub use gen::*;
pub use length::*;
@@ -35,6 +37,9 @@ use std::fmt::{self, Debug, Display, Formatter};
use std::iter::Sum;
use std::ops::*;
+use decorum::N64;
+use serde::{Deserialize, Serialize};
+
/// Generic access to a structure's components.
pub trait Get<Index> {
/// The structure's component type.
diff --git a/src/geom/path.rs b/src/geom/path.rs
index f4e4a8b2..bc0d3f2d 100644
--- a/src/geom/path.rs
+++ b/src/geom/path.rs
@@ -1,7 +1,5 @@
use super::*;
-use serde::{Deserialize, Serialize};
-
/// A bezier path.
#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
diff --git a/src/geom/point.rs b/src/geom/point.rs
index 02f1d277..8ab043c3 100644
--- a/src/geom/point.rs
+++ b/src/geom/point.rs
@@ -1,7 +1,5 @@
use super::*;
-use serde::{Deserialize, Serialize};
-
/// A point in 2D.
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct Point {
diff --git a/src/geom/relative.rs b/src/geom/relative.rs
index 7785788c..056af206 100644
--- a/src/geom/relative.rs
+++ b/src/geom/relative.rs
@@ -1,5 +1,3 @@
-use decorum::N64;
-
use super::*;
/// A relative length.
diff --git a/src/geom/size.rs b/src/geom/size.rs
index 44ceea36..506e1c8f 100644
--- a/src/geom/size.rs
+++ b/src/geom/size.rs
@@ -1,7 +1,5 @@
use super::*;
-use serde::{Deserialize, Serialize};
-
/// A size in 2D.
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct Size {
diff --git a/src/layout/frame.rs b/src/layout/frame.rs
index 0c307dd4..e52e2751 100644
--- a/src/layout/frame.rs
+++ b/src/layout/frame.rs
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use super::{Constrained, Constraints};
use crate::color::Color;
use crate::font::FaceId;
-use crate::geom::{Length, Path, Point, Size};
+use crate::geom::{Em, Length, Path, Point, Size};
use crate::image::ImageId;
/// A finished layout with elements at fixed positions.
@@ -130,27 +130,15 @@ pub struct Text {
pub glyphs: Vec<Glyph>,
}
-impl Text {
- /// Encode the glyph ids into a big-endian byte buffer.
- pub fn encode_glyphs_be(&self) -> Vec<u8> {
- let mut bytes = Vec::with_capacity(2 * self.glyphs.len());
- for glyph in &self.glyphs {
- bytes.push((glyph.id >> 8) as u8);
- bytes.push((glyph.id & 0xff) as u8);
- }
- bytes
- }
-}
-
/// A glyph in a run of shaped text.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Glyph {
/// The glyph's index in the face.
pub id: u16,
/// The advance width of the glyph.
- pub x_advance: Length,
+ pub x_advance: Em,
/// The horizontal offset of the glyph.
- pub x_offset: Length,
+ pub x_offset: Em,
}
/// A geometric shape.
diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs
index fad7f234..efee1591 100644
--- a/src/layout/shaping.rs
+++ b/src/layout/shaping.rs
@@ -6,7 +6,7 @@ use rustybuzz::UnicodeBuffer;
use super::{Element, Frame, Glyph, LayoutContext, Text};
use crate::eval::{FontState, LineState};
use crate::font::{Face, FaceId, FontVariant, LineMetrics};
-use crate::geom::{Dir, Length, Point, Size};
+use crate::geom::{Dir, Em, Length, Point, Size};
use crate::layout::Geometry;
use crate::util::SliceExt;
@@ -72,9 +72,9 @@ pub struct ShapedGlyph {
/// The glyph's index in the face.
pub glyph_id: u16,
/// The advance width of the glyph.
- pub x_advance: Length,
+ pub x_advance: Em,
/// The horizontal offset of the glyph.
- pub x_offset: Length,
+ pub x_offset: Em,
/// The start index of the glyph in the source text.
pub text_index: usize,
/// Whether splitting the shaping result before this glyph would yield the
@@ -106,7 +106,7 @@ impl<'a> ShapedText<'a> {
x_advance: glyph.x_advance,
x_offset: glyph.x_offset,
});
- width += glyph.x_advance;
+ width += glyph.x_advance.to_length(text.size);
}
frame.push(pos, Element::Text(text));
@@ -267,8 +267,8 @@ fn shape_segment<'a>(
glyphs.push(ShapedGlyph {
face_id,
glyph_id: info.glyph_id as u16,
- x_advance: face.to_em(pos[i].x_advance).to_length(size),
- x_offset: face.to_em(pos[i].x_offset).to_length(size),
+ x_advance: face.to_em(pos[i].x_advance),
+ x_offset: face.to_em(pos[i].x_offset),
text_index: base + cluster,
safe_to_break: !info.unsafe_to_break(),
});
@@ -342,7 +342,9 @@ fn measure(
let mut width = Length::zero();
let mut top = Length::zero();
let mut bottom = Length::zero();
- let mut expand_vertical = |face: &Face| {
+
+ // Expand top and bottom by reading the face's vertical metrics.
+ let mut expand = |face: &Face| {
top.set_max(face.vertical_metric(state.top_edge).to_length(state.size));
bottom.set_max(-face.vertical_metric(state.bottom_edge).to_length(state.size));
};
@@ -352,17 +354,17 @@ fn measure(
// first available font.
for family in state.families() {
if let Some(face_id) = ctx.fonts.select(family, state.variant) {
- expand_vertical(ctx.fonts.get(face_id));
+ expand(ctx.fonts.get(face_id));
break;
}
}
} else {
for (face_id, group) in glyphs.group_by_key(|g| g.face_id) {
let face = ctx.fonts.get(face_id);
- expand_vertical(face);
+ expand(face);
for glyph in group {
- width += glyph.x_advance;
+ width += glyph.x_advance.to_length(state.size);
}
}
}