Fix top-left text alignment 📐

This commit is contained in:
Laurenz 2019-06-22 12:51:06 +02:00
parent f6fe3b5cdd
commit c7ee2b393a
8 changed files with 71 additions and 56 deletions

View File

@ -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),
}

View File

@ -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.

View File

@ -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)

View File

@ -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);
}

View File

@ -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);

View File

@ -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
}
}

View File

@ -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,
})
}

View File

@ -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 {