summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRazrFalcon <razrfalcon@gmail.com>2018-05-29 13:52:14 +0300
committerRazrFalcon <razrfalcon@gmail.com>2018-05-29 13:52:14 +0300
commit744f60f8278455e8d7ec78bc602ec7c2c0d43c59 (patch)
tree7bfeeaa4142f80004cba9e1bb8c7c1b56e7340c1 /src
parentc0fcac4258c0c6c4ffb96d828c2871a3805d116b (diff)
Refactoring.
(cairo-backend) Fixed text layout.
Diffstat (limited to 'src')
-rw-r--r--src/backend_cairo/mod.rs3
-rw-r--r--src/backend_cairo/text.rs200
-rw-r--r--src/backend_qt/mod.rs8
-rw-r--r--src/backend_qt/text.rs186
-rw-r--r--src/backend_utils/mod.rs1
-rw-r--r--src/backend_utils/text.rs161
6 files changed, 254 insertions, 305 deletions
diff --git a/src/backend_cairo/mod.rs b/src/backend_cairo/mod.rs
index b945284..187dc59 100644
--- a/src/backend_cairo/mod.rs
+++ b/src/backend_cairo/mod.rs
@@ -375,8 +375,9 @@ fn _calc_node_bbox(
}
usvg::NodeKind::Text(ref text) => {
let mut bbox = Rect::new_bbox();
+ let mut fm = text::PangoFontMetrics::new(opt, cr);
- text::draw_blocks(text, node, opt, cr, |block| {
+ text::draw_blocks(text, node, &mut fm, |block| {
cr.new_path();
let context = text::init_pango_context(opt, cr);
diff --git a/src/backend_cairo/text.rs b/src/backend_cairo/text.rs
index 2bbf244..5810602 100644
--- a/src/backend_cairo/text.rs
+++ b/src/backend_cairo/text.rs
@@ -5,7 +5,6 @@
use std::f64;
// external
-use unicode_segmentation::UnicodeSegmentation;
use cairo;
use pango::{
self,
@@ -18,11 +17,16 @@ use usvg::prelude::*;
// self
use super::prelude::*;
+use backend_utils::text::{
+ self,
+ FontMetrics,
+};
use super::{
fill,
stroke,
};
+pub use backend_utils::text::draw_blocks;
trait PangoScale {
fn scale(&self) -> f64;
@@ -34,15 +38,42 @@ impl PangoScale for i32 {
}
}
+pub struct PangoFontMetrics {
+ layout: pango::Layout,
+ dpi: f64,
+}
+
+impl PangoFontMetrics {
+ pub fn new(opt: &Options, cr: &cairo::Context) -> Self {
+ let context = init_pango_context(opt, cr);
+ let layout = pango::Layout::new(&context);
+ PangoFontMetrics { layout, dpi: opt.usvg.dpi }
+ }
+}
+
+impl FontMetrics<pango::FontDescription> for PangoFontMetrics {
+ fn set_font(&mut self, font: &usvg::Font) {
+ let font = init_font(font, self.dpi);
+ self.layout.set_font_description(&font);
+ }
+
+ fn font(&self) -> pango::FontDescription {
+ self.layout.get_font_description().unwrap()
+ }
+
+ fn width(&self, text: &str) -> f64 {
+ self.layout.set_text(text);
+ self.layout.get_size().0.scale()
+ }
+
+ fn ascent(&self) -> f64 {
+ let mut layout_iter = self.layout.get_iter().unwrap();
+ layout_iter.get_baseline().scale()
+ }
-pub struct TextBlock {
- pub text: String,
- pub bbox: Rect,
- pub rotate: f64,
- pub fill: Option<usvg::Fill>,
- pub stroke: Option<usvg::Stroke>,
- pub font: pango::FontDescription,
- pub decoration: usvg::TextDecoration,
+ fn height(&self) -> f64 {
+ self.layout.get_size().1.scale()
+ }
}
pub fn draw(
@@ -51,158 +82,15 @@ pub fn draw(
cr: &cairo::Context,
) -> Rect {
let tree = &node.tree();
+ let mut fm = PangoFontMetrics::new(opt, cr);
+
if let usvg::NodeKind::Text(ref text) = *node.borrow() {
- draw_blocks(text, node, opt, cr, |block| draw_block(tree, block, opt, cr))
+ draw_blocks(text, node, &mut fm, |block| draw_block(tree, block, opt, cr))
} else {
unreachable!();
}
}
-// TODO: find a way to merge this with a Qt backend
-pub fn draw_blocks<DrawAt>(
- text_kind: &usvg::Text,
- node: &usvg::Node,
- opt: &Options,
- cr: &cairo::Context,
- mut draw: DrawAt,
-) -> Rect
- where DrawAt: FnMut(&TextBlock)
-{
- fn first_number_or(list: &Option<usvg::NumberList>, def: f64) -> f64 {
- list.as_ref().map(|list| list[0]).unwrap_or(def)
- }
-
- let mut blocks: Vec<TextBlock> = Vec::new();
- let mut last_x = 0.0;
- let mut last_y = 0.0;
- for chunk_node in node.children() {
- let kind = chunk_node.borrow();
- let chunk = match *kind {
- usvg::NodeKind::TextChunk(ref chunk) => chunk,
- _ => continue,
- };
-
- let mut chunk_x = first_number_or(&chunk.x, last_x);
- let mut x = chunk_x;
- let mut y = first_number_or(&chunk.y, last_y);
- let start_idx = blocks.len();
- let mut grapheme_idx = 0;
-
- for tspan_node in chunk_node.children() {
- let kind = tspan_node.borrow();
- let tspan = match *kind {
- usvg::NodeKind::TSpan(ref tspan) => tspan,
- _ => continue,
- };
-
- let context = init_pango_context(opt, cr);
- let font = init_font(&tspan.font, opt.usvg.dpi);
- let layout = pango::Layout::new(&context);
- layout.set_font_description(&font);
-
- let iter = UnicodeSegmentation::graphemes(tspan.text.as_str(), true);
- for (i, c) in iter.enumerate() {
- let mut has_custom_offset = i == 0;
-
- {
- let mut number_at = |list: &Option<usvg::NumberList>| -> Option<f64> {
- if let &Some(ref list) = list {
- if let Some(n) = list.get(grapheme_idx) {
- has_custom_offset = true;
- return Some(*n);
- }
- }
-
- None
- };
-
- if let Some(n) = number_at(&chunk.x) { x = n; }
- if let Some(n) = number_at(&chunk.y) { y = n; }
- if let Some(n) = number_at(&chunk.dx) { x += n; }
- if let Some(n) = number_at(&chunk.dy) { y += n; }
-
- if i == 0 {
- if let Some(n) = number_at(&chunk.x) { chunk_x = n; }
- if let Some(n) = number_at(&chunk.dx) { chunk_x += n; }
- }
- }
-
- if text_kind.rotate.is_some() {
- has_custom_offset = true;
- }
-
- let can_merge = !blocks.is_empty() && !has_custom_offset;
- if can_merge {
- let prev_idx = blocks.len() - 1;
- blocks[prev_idx].text.push_str(c);
-
- layout.set_text(&blocks[prev_idx].text);
- let w = layout.get_size().0.scale();
- blocks[prev_idx].bbox.width = w;
-
- let mut new_w = chunk_x;
- for i in start_idx..blocks.len() {
- new_w += blocks[i].bbox.width;
- }
-
- x = new_w;
- } else {
- let mut layout_iter = layout.get_iter().unwrap();
- let yy = y - layout_iter.get_baseline().scale();
- let height = layout.get_size().1.scale();
-
- layout.set_text(c);
- let width = layout.get_size().0.scale();
-
- let bbox = Rect { x, y: yy, width, height };
- x += width;
-
- let rotate = match text_kind.rotate {
- Some(ref list) => { list[blocks.len()] }
- None => 0.0,
- };
-
- blocks.push(TextBlock {
- text: c.to_string(),
- bbox,
- rotate,
- fill: tspan.fill.clone(),
- stroke: tspan.stroke.clone(),
- font: font.clone(),
- decoration: tspan.decoration.clone(),
- });
- }
-
- grapheme_idx += 1;
- }
- }
-
- let mut chunk_w = 0.0;
- for i in start_idx..blocks.len() {
- chunk_w += blocks[i].bbox.width;
- }
-
- let adx = utils::process_text_anchor(chunk.anchor, chunk_w);
- for i in start_idx..blocks.len() {
- blocks[i].bbox.x -= adx;
- }
-
- last_x = chunk_x + chunk_w - adx;
- last_y = y;
- }
-
- let mut bbox = Rect::new_bbox();
- for block in blocks {
- bbox.expand(block.bbox);
- draw(&block);
- }
-
- if bbox.x == f64::MAX { bbox.x = 0.0; }
- if bbox.y == f64::MAX { bbox.y = 0.0; }
-
- bbox
-}
-
pub fn init_pango_context(opt: &Options, cr: &cairo::Context) -> pango::Context {
let context = pc::create_context(cr).unwrap();
pc::update_context(cr, &context);
@@ -223,7 +111,7 @@ pub fn init_pango_layout(
fn draw_block(
tree: &usvg::Tree,
- block: &TextBlock,
+ block: &text::TextBlock<pango::FontDescription>,
opt: &Options,
cr: &cairo::Context,
) {
diff --git a/src/backend_qt/mod.rs b/src/backend_qt/mod.rs
index b29a42f..8955aec 100644
--- a/src/backend_qt/mod.rs
+++ b/src/backend_qt/mod.rs
@@ -323,12 +323,13 @@ pub fn calc_node_bbox(
let p = qt::Painter::new(&img);
let abs_ts = utils::abs_transform(node);
- _calc_node_bbox(node, abs_ts, &p)
+ _calc_node_bbox(node, abs_ts, opt, &p)
}
fn _calc_node_bbox(
node: &usvg::Node,
ts: usvg::Transform,
+ opt: &Options,
p: &qt::Painter,
) -> Option<Rect> {
let mut ts2 = ts;
@@ -340,8 +341,9 @@ fn _calc_node_bbox(
}
usvg::NodeKind::Text(ref text) => {
let mut bbox = Rect::new_bbox();
+ let mut fm = text::QtFontMetrics::new(p);
- text::draw_blocks(text, node, p, |block| {
+ text::draw_blocks(text, node, &mut fm, |block| {
let mut p_path = qt::PainterPath::new();
p.set_font(&block.font);
@@ -365,7 +367,7 @@ fn _calc_node_bbox(
let mut bbox = Rect::new_bbox();
for child in node.children() {
- if let Some(c_bbox) = _calc_node_bbox(&child, ts2, p) {
+ if let Some(c_bbox) = _calc_node_bbox(&child, ts2, opt, p) {
bbox.expand(c_bbox);
}
}
diff --git a/src/backend_qt/text.rs b/src/backend_qt/text.rs
index bd919c3..3919764 100644
--- a/src/backend_qt/text.rs
+++ b/src/backend_qt/text.rs
@@ -3,26 +3,55 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// external
-use unicode_segmentation::UnicodeSegmentation;
use qt;
use usvg;
use usvg::prelude::*;
// self
use super::prelude::*;
+use backend_utils::text::{
+ self,
+ FontMetrics,
+};
use super::{
fill,
stroke,
};
-pub struct TextBlock {
- pub text: String,
- pub bbox: Rect,
- pub rotate: f64,
- pub fill: Option<usvg::Fill>,
- pub stroke: Option<usvg::Stroke>,
- pub font: qt::Font,
- pub decoration: usvg::TextDecoration,
+pub use backend_utils::text::draw_blocks;
+
+
+pub struct QtFontMetrics<'a> {
+ p: &'a qt::Painter,
+}
+
+impl<'a> QtFontMetrics<'a> {
+ pub fn new(p: &'a qt::Painter) -> Self {
+ QtFontMetrics { p }
+ }
+}
+
+impl<'a> FontMetrics<qt::Font> for QtFontMetrics<'a> {
+ fn set_font(&mut self, font: &usvg::Font) {
+ let font = init_font(font);
+ self.p.set_font(&font);
+ }
+
+ fn font(&self) -> qt::Font {
+ self.p.font()
+ }
+
+ fn width(&self, text: &str) -> f64 {
+ self.p.font_metrics().width(text)
+ }
+
+ fn ascent(&self) -> f64 {
+ self.p.font_metrics().ascent()
+ }
+
+ fn height(&self) -> f64 {
+ self.p.font_metrics().height()
+ }
}
pub fn draw(
@@ -31,151 +60,18 @@ pub fn draw(
p: &qt::Painter,
) -> Rect {
let tree = &node.tree();
+ let mut fm = QtFontMetrics::new(p);
if let usvg::NodeKind::Text(ref text) = *node.borrow() {
- draw_blocks(text, node, p, |block| draw_block(tree, block, opt, p))
+ draw_blocks(text, node, &mut fm, |block| draw_block(tree, block, opt, p))
} else {
unreachable!();
}
}
-// TODO: find a way to merge this with a cairo backend
-pub fn draw_blocks<DrawAt>(
- text_kind: &usvg::Text,
- node: &usvg::Node,
- p: &qt::Painter,
- mut draw: DrawAt
-) -> Rect
- where DrawAt: FnMut(&TextBlock)
-{
- fn first_number_or(list: &Option<usvg::NumberList>, def: f64) -> f64 {
- list.as_ref().map(|list| list[0]).unwrap_or(def)
- }
-
- let mut blocks: Vec<TextBlock> = Vec::new();
- let mut last_x = 0.0;
- let mut last_y = 0.0;
- for chunk_node in node.children() {
- let kind = chunk_node.borrow();
- let chunk = match *kind {
- usvg::NodeKind::TextChunk(ref chunk) => chunk,
- _ => continue,
- };
-
- let mut chunk_x = first_number_or(&chunk.x, last_x);
- let mut x = chunk_x;
- let mut y = first_number_or(&chunk.y, last_y);
- let start_idx = blocks.len();
- let mut grapheme_idx = 0;
-
- for tspan_node in chunk_node.children() {
- let kind = tspan_node.borrow();
- let tspan = match *kind {
- usvg::NodeKind::TSpan(ref tspan) => tspan,
- _ => continue,
- };
-
- let font = init_font(&tspan.font);
- p.set_font(&font);
- let font_metrics = p.font_metrics();
-
- let iter = UnicodeSegmentation::graphemes(tspan.text.as_str(), true);
- for (i, c) in iter.enumerate() {
- let mut has_custom_offset = i == 0;
-
- {
- let mut number_at = |list: &Option<usvg::NumberList>| -> Option<f64> {
- if let &Some(ref list) = list {
- if let Some(n) = list.get(grapheme_idx) {
- has_custom_offset = true;
- return Some(*n);
- }
- }
-
- None
- };
-
- if let Some(n) = number_at(&chunk.x) { x = n; }
- if let Some(n) = number_at(&chunk.y) { y = n; }
- if let Some(n) = number_at(&chunk.dx) { x += n; }
- if let Some(n) = number_at(&chunk.dy) { y += n; }
-
- if i == 0 {
- if let Some(n) = number_at(&chunk.x) { chunk_x = n; }
- if let Some(n) = number_at(&chunk.dx) { chunk_x += n; }
- }
- }
-
- if text_kind.rotate.is_some() {
- has_custom_offset = true;
- }
-
- let can_merge = !blocks.is_empty() && !has_custom_offset;
- if can_merge {
- let prev_idx = blocks.len() - 1;
- blocks[prev_idx].text.push_str(c);
- let w = font_metrics.width(&blocks[prev_idx].text);
- blocks[prev_idx].bbox.width = w;
-
- let mut new_w = chunk_x;
- for i in start_idx..blocks.len() {
- new_w += blocks[i].bbox.width;
- }
-
- x = new_w;
- } else {
- let yy = y - font_metrics.ascent();
- let height = font_metrics.height();
- let width = font_metrics.width(c);
- let bbox = Rect { x, y: yy, width, height };
- x += width;
-
- let rotate = match text_kind.rotate {
- Some(ref list) => { list[blocks.len()] }
- None => 0.0,
- };
-
- blocks.push(TextBlock {
- text: c.to_string(),
- bbox,
- rotate,
- fill: tspan.fill.clone(),
- stroke: tspan.stroke.clone(),
- font: init_font(&tspan.font), // TODO: clone
- decoration: tspan.decoration.clone(),
- });
- }
-
- grapheme_idx += 1;
- }
- }
-
- let mut chunk_w = 0.0;
- for i in start_idx..blocks.len() {
- chunk_w += blocks[i].bbox.width;
- }
-
- let adx = utils::process_text_anchor(chunk.anchor, chunk_w);
- for i in start_idx..blocks.len() {
- blocks[i].bbox.x -= adx;
- }
-
- last_x = chunk_x + chunk_w - adx;
- last_y = y;
- }
-
- let mut bbox = Rect::new_bbox();
- for block in blocks {
- bbox.expand(block.bbox);
- draw(&block);
- }
-
- bbox
-}
-
fn draw_block(
tree: &usvg::Tree,
- block: &TextBlock,
+ block: &text::TextBlock<qt::Font>,
opt: &Options,
p: &qt::Painter,
) {
diff --git a/src/backend_utils/mod.rs b/src/backend_utils/mod.rs
index 97f54aa..37a18d0 100644
--- a/src/backend_utils/mod.rs
+++ b/src/backend_utils/mod.rs
@@ -4,3 +4,4 @@
pub mod image;
pub mod mask;
+pub mod text;
diff --git a/src/backend_utils/text.rs b/src/backend_utils/text.rs
new file mode 100644
index 0000000..82ac48e
--- /dev/null
+++ b/src/backend_utils/text.rs
@@ -0,0 +1,161 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// external
+use unicode_segmentation::UnicodeSegmentation;
+use usvg;
+
+// self
+use geom::*;
+use utils;
+
+
+pub struct TextBlock<Font> {
+ pub text: String,
+ pub bbox: Rect,
+ pub rotate: f64,
+ pub fill: Option<usvg::Fill>,
+ pub stroke: Option<usvg::Stroke>,
+ pub font: Font,
+ pub decoration: usvg::TextDecoration,
+}
+
+pub trait FontMetrics<Font> {
+ fn set_font(&mut self, font: &usvg::Font);
+ fn font(&self) -> Font;
+ fn width(&self, text: &str) -> f64;
+ fn ascent(&self) -> f64;
+ fn height(&self) -> f64;
+}
+
+pub fn draw_blocks<Font, DrawAt>(
+ text_kind: &usvg::Text,
+ node: &usvg::Node,
+ font_metrics: &mut FontMetrics<Font>,
+ mut draw: DrawAt
+) -> Rect
+ where DrawAt: FnMut(&TextBlock<Font>)
+{
+ fn first_number_or(list: &Option<usvg::NumberList>, def: f64) -> f64 {
+ list.as_ref().map(|list| list[0]).unwrap_or(def)
+ }
+
+ let mut blocks: Vec<TextBlock<Font>> = Vec::new();
+ let mut last_x = 0.0;
+ let mut last_y = 0.0;
+ for chunk_node in node.children() {
+ let kind = chunk_node.borrow();
+ let chunk = match *kind {
+ usvg::NodeKind::TextChunk(ref chunk) => chunk,
+ _ => continue,
+ };
+
+ let mut chunk_x = first_number_or(&chunk.x, last_x);
+ let mut x = chunk_x;
+ let mut y = first_number_or(&chunk.y, last_y);
+ let start_idx = blocks.len();
+ let mut grapheme_idx = 0;
+
+ for tspan_node in chunk_node.children() {
+ let kind = tspan_node.borrow();
+ let tspan = match *kind {
+ usvg::NodeKind::TSpan(ref tspan) => tspan,
+ _ => continue,
+ };
+
+ font_metrics.set_font(&tspan.font);
+
+ let iter = UnicodeSegmentation::graphemes(tspan.text.as_str(), true);
+ for (i, c) in iter.enumerate() {
+ let mut has_custom_offset = i == 0;
+
+ {
+ let mut number_at = |list: &Option<usvg::NumberList>| -> Option<f64> {
+ if let &Some(ref list) = list {
+ if let Some(n) = list.get(grapheme_idx) {
+ has_custom_offset = true;
+ return Some(*n);
+ }
+ }
+
+ None
+ };
+
+ if let Some(n) = number_at(&chunk.x) { x = n; }
+ if let Some(n) = number_at(&chunk.y) { y = n; }
+ if let Some(n) = number_at(&chunk.dx) { x += n; }
+ if let Some(n) = number_at(&chunk.dy) { y += n; }
+
+ if i == 0 {
+ if let Some(n) = number_at(&chunk.x) { chunk_x = n; }
+ if let Some(n) = number_at(&chunk.dx) { chunk_x += n; }
+ }
+ }
+
+ if text_kind.rotate.is_some() {
+ has_custom_offset = true;
+ }
+
+ let can_merge = !blocks.is_empty() && !has_custom_offset;
+ if can_merge {
+ let prev_idx = blocks.len() - 1;
+ blocks[prev_idx].text.push_str(c);
+ let w = font_metrics.width(&blocks[prev_idx].text);
+ blocks[prev_idx].bbox.width = w;
+
+ let mut new_w = chunk_x;
+ for i in start_idx..blocks.len() {
+ new_w += blocks[i].bbox.width;
+ }
+
+ x = new_w;
+ } else {
+ let width = font_metrics.width(c);
+ let yy = y - font_metrics.ascent();
+ let height = font_metrics.height();
+ let bbox = Rect { x, y: yy, width, height };
+ x += width;
+
+ let rotate = match text_kind.rotate {
+ Some(ref list) => { list[blocks.len()] }
+ None => 0.0,
+ };
+
+ blocks.push(TextBlock {
+ text: c.to_string(),
+ bbox,
+ rotate,
+ fill: tspan.fill.clone(),
+ stroke: tspan.stroke.clone(),
+ font: font_metrics.font(),
+ decoration: tspan.decoration.clone(),
+ });
+ }
+
+ grapheme_idx += 1;
+ }
+ }
+
+ let mut chunk_w = 0.0;
+ for i in start_idx..blocks.len() {
+ chunk_w += blocks[i].bbox.width;
+ }
+
+ let adx = utils::process_text_anchor(chunk.anchor, chunk_w);
+ for i in start_idx..blocks.len() {
+ blocks[i].bbox.x -= adx;
+ }
+
+ last_x = chunk_x + chunk_w - adx;
+ last_y = y;
+ }
+
+ let mut bbox = Rect::new_bbox();
+ for block in blocks {
+ bbox.expand(block.bbox);
+ draw(&block);
+ }
+
+ bbox
+}