Fix top-left text alignment 📐
This commit is contained in:
parent
f6fe3b5cdd
commit
c7ee2b393a
14
src/doc.rs
14
src/doc.rs
@ -20,19 +20,17 @@ pub struct Page {
|
||||
pub width: Size,
|
||||
/// The height of the page.
|
||||
pub height: Size,
|
||||
/// Text actions specifying how to draw text content on the page.
|
||||
pub actions: Vec<TextAction>,
|
||||
/// Layouting actions specifying how to draw content on the page.
|
||||
pub actions: Vec<LayoutAction>,
|
||||
}
|
||||
|
||||
/// A text layouting action.
|
||||
/// A layouting action.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TextAction {
|
||||
pub enum LayoutAction {
|
||||
/// Move to an absolute position.
|
||||
MoveAbsolute(Size2D),
|
||||
/// Move from the _start_ of the current line by an (x, y) offset.
|
||||
MoveNewline(Size2D),
|
||||
/// Write text starting at the current position.
|
||||
WriteText(String),
|
||||
/// Set the font by index and font size.
|
||||
SetFont(usize, f32),
|
||||
/// Write text starting at the current position.
|
||||
WriteText(String),
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ use pdf::doc::{Catalog, PageTree, Page, Resource, Text};
|
||||
use pdf::font::{Type0Font, CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags};
|
||||
use pdf::font::{GlyphUnit, CMap, CMapEncoding, WidthRecord, FontStream};
|
||||
|
||||
use crate::doc::{Document, Page as DocPage, TextAction};
|
||||
use crate::doc::{Document, Page as DocPage, LayoutAction};
|
||||
use crate::font::{Font, FontError};
|
||||
use crate::size::Size;
|
||||
use crate::size::{Size, Size2D};
|
||||
|
||||
|
||||
/// Exports documents into _PDFs_.
|
||||
@ -72,8 +72,8 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
||||
for page in &doc.pages {
|
||||
for action in &page.actions {
|
||||
match action {
|
||||
TextAction::WriteText(string) => chars[font].extend(string.chars()),
|
||||
TextAction::SetFont(id, _) => font = *id,
|
||||
LayoutAction::WriteText(string) => chars[font].extend(string.chars()),
|
||||
LayoutAction::SetFont(id, _) => font = *id,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -123,7 +123,7 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
||||
// The page objects
|
||||
for (id, page) in ids(self.offsets.pages).zip(&self.doc.pages) {
|
||||
self.writer.write_obj(id, Page::new(self.offsets.page_tree)
|
||||
.media_box(Rect::new(0.0, 0.0, page.width.to_points(), page.height.to_points()))
|
||||
.media_box(Rect::new(0.0, 0.0, page.width.to_pt(), page.height.to_pt()))
|
||||
.contents(ids(self.offsets.contents))
|
||||
)?;
|
||||
}
|
||||
@ -141,23 +141,40 @@ impl<'d, W: Write> PdfEngine<'d, W> {
|
||||
|
||||
/// Write the content of a page.
|
||||
fn write_page(&mut self, id: u32, page: &DocPage) -> PdfResult<()> {
|
||||
let mut font = 0;
|
||||
let mut text = Text::new();
|
||||
// The currently used font.
|
||||
let mut active_font = (std::usize::MAX, 0.0);
|
||||
|
||||
text.tm(1.0, 0.0, 0.0, 1.0, 0.0, page.height.to_points());
|
||||
// The last set position and font, these get flushed when content is written.
|
||||
let mut next_pos = Some(Size2D::zero());
|
||||
let mut next_font = None;
|
||||
|
||||
// The output text.
|
||||
let mut text = Text::new();
|
||||
|
||||
for action in &page.actions {
|
||||
match action {
|
||||
TextAction::MoveAbsolute(pos) => {
|
||||
let x = pos.x.to_points();
|
||||
let y = (page.height - pos.y).to_points();
|
||||
text.tm(1.0, 0.0, 0.0, 1.0, x, y);
|
||||
},
|
||||
TextAction::MoveNewline(pos) => { text.td(pos.x.to_points(), -pos.y.to_points()); },
|
||||
TextAction::WriteText(string) => { text.tj(self.fonts[font].encode(&string)); },
|
||||
TextAction::SetFont(id, size) => {
|
||||
font = *id;
|
||||
text.tf(*id as u32 + 1, *size);
|
||||
LayoutAction::MoveAbsolute(pos) => next_pos = Some(*pos),
|
||||
LayoutAction::SetFont(id, size) => next_font = Some((*id, *size)),
|
||||
LayoutAction::WriteText(string) => {
|
||||
// Flush the font if it is different from the current.
|
||||
if let Some((id, size)) = next_font {
|
||||
if (id, size) != active_font {
|
||||
text.tf(id as u32 + 1, size);
|
||||
active_font = (id, size);
|
||||
next_font = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the position.
|
||||
if let Some(pos) = next_pos {
|
||||
let x = pos.x.to_pt();
|
||||
let y = (page.height - pos.y - Size::pt(active_font.1)).to_pt();
|
||||
text.tm(1.0, 0.0, 0.0, 1.0, x, y);
|
||||
next_pos = None;
|
||||
}
|
||||
|
||||
// Write the text.
|
||||
text.tj(self.fonts[active_font.0].encode(&string));
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -249,7 +266,7 @@ impl PdfFont {
|
||||
fn new(font: &Font, chars: &HashSet<char>) -> PdfResult<PdfFont> {
|
||||
/// Convert a size into a _PDF_ glyph unit.
|
||||
fn size_to_glyph_unit(size: Size) -> GlyphUnit {
|
||||
(1000.0 * size.to_points()).round() as GlyphUnit
|
||||
(1000.0 * size.to_pt()).round() as GlyphUnit
|
||||
}
|
||||
|
||||
// Subset the font using the selected characters.
|
||||
|
@ -60,7 +60,7 @@ impl Font {
|
||||
|
||||
// Create a conversion function between font units and sizes.
|
||||
let font_unit_ratio = 1.0 / (head.units_per_em as f32);
|
||||
let font_unit_to_size = |x| Size::points(font_unit_ratio * x as f32);
|
||||
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x as f32);
|
||||
|
||||
// Find out the name of the font.
|
||||
let font_name = name.get_decoded(NameEntry::PostScriptName)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Block-style layouting of boxes.
|
||||
|
||||
use crate::doc::{Document, Page, TextAction};
|
||||
use crate::doc::{Document, Page, LayoutAction};
|
||||
use crate::font::Font;
|
||||
use crate::size::{Size, Size2D};
|
||||
use super::{ActionList, LayoutSpace, LayoutResult, LayoutError};
|
||||
@ -12,7 +12,7 @@ pub struct BoxLayout {
|
||||
/// The size of the box.
|
||||
pub dimensions: Size2D,
|
||||
/// The actions composing this layout.
|
||||
pub actions: Vec<TextAction>,
|
||||
pub actions: Vec<LayoutAction>,
|
||||
}
|
||||
|
||||
impl BoxLayout {
|
||||
@ -90,7 +90,7 @@ impl BoxLayouter {
|
||||
pub fn add_box_absolute(&mut self, position: Size2D, layout: BoxLayout) {
|
||||
// Move all actions into this layout and translate absolute positions.
|
||||
self.actions.reset_origin();
|
||||
self.actions.add(TextAction::MoveAbsolute(position));
|
||||
self.actions.add(LayoutAction::MoveAbsolute(position));
|
||||
self.actions.set_origin(position);
|
||||
self.actions.extend(layout.actions);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Flexible and lazy layouting of boxes.
|
||||
|
||||
use crate::doc::TextAction;
|
||||
use crate::doc::LayoutAction;
|
||||
use crate::size::{Size, Size2D};
|
||||
use super::{BoxLayout, ActionList, LayoutSpace, LayoutResult, LayoutError};
|
||||
|
||||
@ -157,7 +157,7 @@ impl FlexFinisher {
|
||||
fn append(&mut self, layout: BoxLayout) {
|
||||
// Move all actions into this layout and translate absolute positions.
|
||||
self.actions.reset_origin();
|
||||
self.actions.add(TextAction::MoveAbsolute(self.cursor));
|
||||
self.actions.add(LayoutAction::MoveAbsolute(self.cursor));
|
||||
self.actions.set_origin(self.cursor);
|
||||
self.actions.extend(layout.actions);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! The layouting engine.
|
||||
|
||||
use crate::doc::TextAction;
|
||||
use crate::doc::LayoutAction;
|
||||
use crate::font::{FontLoader, FontError};
|
||||
use crate::size::{Size, Size2D, SizeBox};
|
||||
use crate::syntax::{SyntaxTree, Node};
|
||||
@ -86,7 +86,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
|
||||
padding: SizeBox::zero(),
|
||||
shrink_to_fit: true,
|
||||
},
|
||||
flex_spacing: (ctx.style.line_spacing - 1.0) * Size::points(ctx.style.font_size),
|
||||
flex_spacing: (ctx.style.line_spacing - 1.0) * Size::pt(ctx.style.font_size),
|
||||
};
|
||||
|
||||
// The mutable context for layouting single pieces of text.
|
||||
@ -192,7 +192,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
|
||||
|
||||
/// Add the spacing between two paragraphs.
|
||||
fn add_paragraph_spacing(&mut self) -> LayoutResult<()> {
|
||||
let size = Size::points(self.text_ctx.style.font_size)
|
||||
let size = Size::pt(self.text_ctx.style.font_size)
|
||||
* (self.text_ctx.style.line_spacing * self.text_ctx.style.paragraph_spacing - 1.0);
|
||||
self.box_layouter.add_space(size)
|
||||
}
|
||||
@ -201,7 +201,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
|
||||
/// Manipulates and optimizes a list of actions.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ActionList {
|
||||
actions: Vec<TextAction>,
|
||||
actions: Vec<LayoutAction>,
|
||||
origin: Size2D,
|
||||
active_font: (usize, f32),
|
||||
}
|
||||
@ -218,8 +218,8 @@ impl ActionList {
|
||||
|
||||
/// Add an action to the list if it is not useless
|
||||
/// (like changing to a font that is already active).
|
||||
pub fn add(&mut self, action: TextAction) {
|
||||
use TextAction::*;
|
||||
pub fn add(&mut self, action: LayoutAction) {
|
||||
use LayoutAction::*;
|
||||
match action {
|
||||
MoveAbsolute(pos) => self.actions.push(MoveAbsolute(self.origin + pos)),
|
||||
SetFont(index, size) => if (index, size) != self.active_font {
|
||||
@ -231,7 +231,7 @@ impl ActionList {
|
||||
}
|
||||
|
||||
/// Add a series of actions.
|
||||
pub fn extend<I>(&mut self, actions: I) where I: IntoIterator<Item=TextAction> {
|
||||
pub fn extend<I>(&mut self, actions: I) where I: IntoIterator<Item=LayoutAction> {
|
||||
for action in actions.into_iter() {
|
||||
self.add(action);
|
||||
}
|
||||
@ -254,7 +254,7 @@ impl ActionList {
|
||||
}
|
||||
|
||||
/// Return the list of actions as a vector.
|
||||
pub fn into_vec(self) -> Vec<TextAction> {
|
||||
pub fn into_vec(self) -> Vec<LayoutAction> {
|
||||
self.actions
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Layouting of text into boxes.
|
||||
|
||||
use crate::doc::TextAction;
|
||||
use crate::doc::LayoutAction;
|
||||
use crate::font::FontQuery;
|
||||
use crate::size::{Size, Size2D};
|
||||
use super::*;
|
||||
@ -39,11 +39,11 @@ pub fn layout(text: &str, ctx: &TextContext) -> LayoutResult<BoxLayout> {
|
||||
// Change the font if necessary.
|
||||
if active_font != index {
|
||||
if !buffer.is_empty() {
|
||||
actions.push(TextAction::WriteText(buffer));
|
||||
actions.push(LayoutAction::WriteText(buffer));
|
||||
buffer = String::new();
|
||||
}
|
||||
|
||||
actions.push(TextAction::SetFont(index, ctx.style.font_size));
|
||||
actions.push(LayoutAction::SetFont(index, ctx.style.font_size));
|
||||
active_font = index;
|
||||
}
|
||||
|
||||
@ -52,11 +52,11 @@ pub fn layout(text: &str, ctx: &TextContext) -> LayoutResult<BoxLayout> {
|
||||
|
||||
// Write the remaining characters.
|
||||
if !buffer.is_empty() {
|
||||
actions.push(TextAction::WriteText(buffer));
|
||||
actions.push(LayoutAction::WriteText(buffer));
|
||||
}
|
||||
|
||||
Ok(BoxLayout {
|
||||
dimensions: Size2D::new(width, Size::points(ctx.style.font_size)),
|
||||
dimensions: Size2D::new(width, Size::pt(ctx.style.font_size)),
|
||||
actions,
|
||||
})
|
||||
}
|
||||
|
20
src/size.rs
20
src/size.rs
@ -42,11 +42,7 @@ impl Size {
|
||||
|
||||
/// Create a size from an amount of points.
|
||||
#[inline]
|
||||
pub fn points(points: f32) -> Size { Size { points } }
|
||||
|
||||
/// Create a size from an amount of inches.
|
||||
#[inline]
|
||||
pub fn inches(inches: f32) -> Size { Size { points: 72.0 * inches } }
|
||||
pub fn pt(points: f32) -> Size { Size { points } }
|
||||
|
||||
/// Create a size from an amount of millimeters.
|
||||
#[inline]
|
||||
@ -56,13 +52,13 @@ impl Size {
|
||||
#[inline]
|
||||
pub fn cm(cm: f32) -> Size { Size { points: 28.3465 * cm } }
|
||||
|
||||
/// Create a size from an amount of inches.
|
||||
#[inline]
|
||||
pub fn inches(inches: f32) -> Size { Size { points: 72.0 * inches } }
|
||||
|
||||
/// Convert this size into points.
|
||||
#[inline]
|
||||
pub fn to_points(&self) -> f32 { self.points }
|
||||
|
||||
/// Convert this size into inches.
|
||||
#[inline]
|
||||
pub fn to_inches(&self) -> f32 { self.points * 0.0138889 }
|
||||
pub fn to_pt(&self) -> f32 { self.points }
|
||||
|
||||
/// Convert this size into millimeters.
|
||||
#[inline]
|
||||
@ -71,6 +67,10 @@ impl Size {
|
||||
/// Convert this size into centimeters.
|
||||
#[inline]
|
||||
pub fn to_cm(&self) -> f32 { self.points * 0.0352778 }
|
||||
|
||||
/// Convert this size into inches.
|
||||
#[inline]
|
||||
pub fn to_inches(&self) -> f32 { self.points * 0.0138889 }
|
||||
}
|
||||
|
||||
impl Size2D {
|
||||
|
Loading…
x
Reference in New Issue
Block a user