From c7ee2b393a369325b3578557e045f2ff94ceab8f Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 22 Jun 2019 12:51:06 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20top-left=20text=20alignment=20?= =?UTF-8?q?=F0=9F=93=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/doc.rs | 14 +++++------- src/export/pdf.rs | 55 +++++++++++++++++++++++++++++---------------- src/font.rs | 2 +- src/layout/boxed.rs | 6 ++--- src/layout/flex.rs | 4 ++-- src/layout/mod.rs | 16 ++++++------- src/layout/text.rs | 10 ++++----- src/size.rs | 20 ++++++++--------- 8 files changed, 71 insertions(+), 56 deletions(-) diff --git a/src/doc.rs b/src/doc.rs index f08332f6b..0b4db73e6 100644 --- a/src/doc.rs +++ b/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, + /// Layouting actions specifying how to draw content on the page. + pub actions: Vec, } -/// 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), } diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 1c0d52b05..a09f3b0c8 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -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) -> PdfResult { /// 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. diff --git a/src/font.rs b/src/font.rs index 9b13ed326..d11fe75c2 100644 --- a/src/font.rs +++ b/src/font.rs @@ -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) diff --git a/src/layout/boxed.rs b/src/layout/boxed.rs index e712f650c..15af278a2 100644 --- a/src/layout/boxed.rs +++ b/src/layout/boxed.rs @@ -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, + pub actions: Vec, } 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); } diff --git a/src/layout/flex.rs b/src/layout/flex.rs index f966eae27..730b876e6 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -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); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 40948a13e..b678ab2bb 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -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, + actions: Vec, 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(&mut self, actions: I) where I: IntoIterator { + pub fn extend(&mut self, actions: I) where I: IntoIterator { 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 { + pub fn into_vec(self) -> Vec { self.actions } } diff --git a/src/layout/text.rs b/src/layout/text.rs index 8aa76c4cd..0a0241a06 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -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 { // 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 { // 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, }) } diff --git a/src/size.rs b/src/size.rs index e1f54a4ff..674abdb5d 100644 --- a/src/size.rs +++ b/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 {