azul_layout/text/mod.rs
1//! General crate for text layout / text shaping
2//!
3//! 
4//!
5//! # Example
6//!
7//! ```rust,ignore,no_run
8//! use azul_text_layout::{
9//! text_layout::{split_text_into_words, words_to_scaled_words},
10//! text_shaping::get_font_metrics_freetype,
11//! };
12//!
13//! let text = "hello";
14//! let font_size = 14.0; // px
15//! let font = include_bytes!("Helvetica.ttf");
16//! let font_index = 0; // only for fonts with font collections
17//! let font_metrics = get_font_metrics_freetype(&font, font_index);
18//! let words = split_text_into_words(text);
19//! let scaled_words = words_to_scaled_words(&words, &font, font_index as u32, font_metrics, font_size);
20//!
21//! let total_width = scaled_words.items.iter().map(|i| i.word_width).sum();
22//! ```
23//!
24//! # Full text layout
25//!
26//! ```rust,ignore,no_run
27//! use azul_text_layout::{text_layout, text_shaping::get_font_metrics_freetype};
28//! use azul_css::{LayoutSize, StyleTextAlignmentHorz};
29//! use azul_core::ui_solver::ResolvedTextLayoutOptions;
30//!
31//! // set all options of the text
32//! let text = "hello";
33//! let font_size = 14.0; // px
34//! let font_bytes = include_bytes!("Helvetica.ttf");
35//! let font_index = 0; // only for fonts with font collections
36//! let text_layout_options = ResolvedTextLayoutOptions {
37//! font_size_px: font_size,
38//! line_height: None,
39//! letter_spacing: None,
40//! word_spacing: None,
41//! tab_width: None,
42//! // for line breaking, maximum width that a line can have
43//! max_horizontal_width: Some(400.0), // px
44//! leading: None,
45//! holes: Vec::new(),
46//! };
47//!
48//! // Cache the font metrics of the given font (baseline, height, etc.)
49//! let font_metrics = get_font_metrics_freetype(font_bytes, font_index as i32);
50//! // "Hello World" => ["Hello", "World"]
51//! let words = text_layout::split_text_into_words(text);
52//! // "Hello" @ 14px => Size { width: 50px, height: 14px }
53//! let scaled_words = text_layout::words_to_scaled_words(&words, font_bytes, font_index, font_metrics, text_layout_options.font_size_px);
54//! // Calculate the origin of the word relative to the line
55//! let word_positions = text_layout::position_words(&words, &scaled_words, &text_layout_options);
56//! // Calculate the origin of the line relative to (0, 0)
57//! let mut inline_text_layout = text_layout::word_positions_to_inline_text_layout(&word_positions, &scaled_words);
58//! // Align the line horizontally
59//! inline_text_layout.align_children_horizontal(StyleTextAlignmentHorz::Center);
60//! // Calculate the glyph positons (line_offset + word_offset + glyph_offset)
61//! let layouted_glyphs = text_layout::get_layouted_glyphs(&word_positions, &scaled_words, &inline_text_layout);
62//!
63//! println!("{:#?}", inline_text_layout); // get infos about word offset, line breaking, etc.
64//! println!("{:#?}", layouted_glyphs); // get the final glyph positions relative to the origin
65//! ```
66
67#![allow(warnings)]
68
69// #![no_std] // doable once allsorts PR is merged
70
71pub mod layout;
72pub mod script;
73pub mod shaping;
74
75use alloc::boxed::Box;
76use core::ffi::c_void;
77
78use azul_core::{
79 app_resources::{LoadedFontSource, ShapedWords, Words},
80 callbacks::DocumentId,
81 id_tree::NodeId,
82 traits::GetTextLayout,
83 ui_solver::{InlineTextLayout, ResolvedTextLayoutOptions},
84};
85use azul_css::{FontData, FontRef};
86
87use self::{layout::FontMetrics, shaping::ParsedFont};
88
89#[derive(Debug, Clone)]
90pub struct InlineText<'a> {
91 pub words: &'a Words,
92 pub shaped_words: &'a ShapedWords,
93}
94
95impl<'a> GetTextLayout for InlineText<'a> {
96 fn get_text_layout(
97 &mut self,
98 _: &DocumentId,
99 _: NodeId,
100 text_layout_options: &ResolvedTextLayoutOptions,
101 ) -> InlineTextLayout {
102 let layouted_text_block =
103 self::layout::position_words(self.words, self.shaped_words, text_layout_options);
104 // TODO: Cache the layouted text block on the &mut self
105 self::layout::word_positions_to_inline_text_layout(&layouted_text_block)
106 }
107}
108
109fn parsed_font_destructor(ptr: *mut c_void) {
110 unsafe {
111 let _ = Box::from_raw(ptr as *mut ParsedFont);
112 }
113}
114
115pub fn parse_font_fn(source: LoadedFontSource) -> Option<FontRef> {
116 self::layout::parse_font(
117 source.data.as_ref(),
118 source.index as usize,
119 source.load_outlines,
120 )
121 .map(|parsed_font| {
122 FontRef::new(FontData {
123 bytes: source.data,
124 font_index: source.index,
125 parsed: Box::into_raw(Box::new(parsed_font)) as *const c_void,
126 parsed_destructor: parsed_font_destructor,
127 })
128 })
129}
130
131pub fn get_font_metrics_fontref(font_ref: &FontRef) -> FontMetrics {
132 let parsed_font = unsafe { &*(font_ref.get_data().parsed as *const ParsedFont) };
133 parsed_font.font_metrics.clone()
134}