pango/functions.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::translate::*;
4use std::{ffi::c_char, ptr};
5
6#[cfg(feature = "v1_44")]
7use crate::ShapeFlags;
8pub use crate::auto::functions::*;
9use crate::{Analysis, AttrIterator, AttrList, Context, Direction, GlyphString, Item, ffi};
10
11/// Reorder items from logical order to visual order.
12///
13/// The visual order is determined from the associated directional
14/// levels of the items. The original list is unmodified.
15///
16/// (Please open a bug if you use this function.
17/// It is not a particularly convenient interface, and the code
18/// is duplicated elsewhere in Pango for that reason.)
19/// ## `items`
20/// a `GList` of [`Item`][crate::Item]
21/// in logical order.
22///
23/// # Returns
24///
25/// a `GList`
26/// of [`Item`][crate::Item] structures in visual order.
27#[doc(alias = "pango_reorder_items")]
28pub fn reorder_items(logical_items: &glib::List<Item>) -> glib::List<Item> {
29 unsafe {
30 FromGlibPtrContainer::from_glib_full(ffi::pango_reorder_items(
31 logical_items.as_ptr() as *mut _
32 ))
33 }
34}
35
36/// Convert the characters in @text into glyphs.
37///
38/// Given a segment of text and the corresponding [`Analysis`][crate::Analysis] structure
39/// returned from [`itemize()`][crate::itemize()], convert the characters into glyphs.
40/// You may also pass in only a substring of the item from [`itemize()`][crate::itemize()].
41///
42/// This is similar to [`shape()`][crate::shape()], except it also can optionally take
43/// the full paragraph text as input, which will then be used to perform
44/// certain cross-item shaping interactions. If you have access to the broader
45/// text of which @item_text is part of, provide the broader text as
46/// @paragraph_text. If @paragraph_text is [`None`], item text is used instead.
47///
48/// Some aspects of hyphen insertion and text transformation (in particular,
49/// capitalization) require log attrs, and thus can only be handled by
50/// `shape_item()`.
51///
52/// Note that the extra attributes in the @analyis that is returned from
53/// [`itemize()`][crate::itemize()] have indices that are relative to the entire paragraph,
54/// so you do not pass the full paragraph text as @paragraph_text, you need
55/// to subtract the item offset from their indices before calling
56/// [`shape_full()`][crate::shape_full()].
57/// ## `item_text`
58/// valid UTF-8 text to shape.
59/// ## `item_length`
60/// the length (in bytes) of @item_text. -1 means nul-terminated text.
61/// ## `paragraph_text`
62/// text of the paragraph (see details).
63/// ## `paragraph_length`
64/// the length (in bytes) of @paragraph_text. -1 means nul-terminated text.
65/// ## `analysis`
66/// [`Analysis`][crate::Analysis] structure from [`itemize()`][crate::itemize()].
67///
68/// # Returns
69///
70///
71/// ## `glyphs`
72/// glyph string in which to store results.
73#[doc(alias = "pango_shape_full")]
74pub fn shape_full(
75 item_text: &str,
76 paragraph_text: Option<&str>,
77 analysis: &Analysis,
78 glyphs: &mut GlyphString,
79) {
80 let item_length = item_text.len() as i32;
81 let paragraph_length = paragraph_text.map(|t| t.len() as i32).unwrap_or_default();
82 let paragraph_ptr = paragraph_text.map_or(ptr::null(), |t| t.as_ptr() as *const c_char);
83 unsafe {
84 // The function does not take null-terminated strings when a length is provided.
85 // It also requires item_text to point to a subsequence of paragraph_text.
86 // Using to_glib_none() on &str will copy the string and cause problems.
87 ffi::pango_shape_full(
88 item_text.as_ptr() as *const c_char,
89 item_length,
90 paragraph_ptr,
91 paragraph_length,
92 analysis.to_glib_none().0,
93 glyphs.to_glib_none_mut().0,
94 );
95 }
96}
97
98/// Convert the characters in @text into glyphs.
99///
100/// Given a segment of text and the corresponding [`Analysis`][crate::Analysis] structure
101/// returned from [`itemize()`][crate::itemize()], convert the characters into glyphs. You
102/// may also pass in only a substring of the item from [`itemize()`][crate::itemize()].
103///
104/// It is recommended that you use [`shape_full()`][crate::shape_full()] instead, since
105/// that API allows for shaping interaction happening across text item
106/// boundaries.
107///
108/// Some aspects of hyphen insertion and text transformation (in particular,
109/// capitalization) require log attrs, and thus can only be handled by
110/// `shape_item()`.
111///
112/// Note that the extra attributes in the @analyis that is returned from
113/// [`itemize()`][crate::itemize()] have indices that are relative to the entire paragraph,
114/// so you need to subtract the item offset from their indices before
115/// calling [`shape()`][crate::shape()].
116/// ## `text`
117/// the text to process
118/// ## `length`
119/// the length (in bytes) of @text
120/// ## `analysis`
121/// [`Analysis`][crate::Analysis] structure from [`itemize()`][crate::itemize()]
122///
123/// # Returns
124///
125///
126/// ## `glyphs`
127/// glyph string in which to store results
128#[doc(alias = "pango_shape")]
129pub fn shape(item_text: &str, analysis: &Analysis, glyphs: &mut GlyphString) {
130 let item_length = item_text.len() as i32;
131 unsafe {
132 // The function does not take null-terminated strings when a length is provided.
133 // Using to_glib_none() on &str will copy the string unnecessarily.
134 ffi::pango_shape(
135 item_text.as_ptr() as *const c_char,
136 item_length,
137 analysis.to_glib_none().0,
138 glyphs.to_glib_none_mut().0,
139 );
140 }
141}
142
143/// Convert the characters in @text into glyphs.
144///
145/// Given a segment of text and the corresponding [`Analysis`][crate::Analysis] structure
146/// returned from [`itemize()`][crate::itemize()], convert the characters into glyphs.
147/// You may also pass in only a substring of the item from [`itemize()`][crate::itemize()].
148///
149/// This is similar to [`shape_full()`][crate::shape_full()], except it also takes flags
150/// that can influence the shaping process.
151///
152/// Some aspects of hyphen insertion and text transformation (in particular,
153/// capitalization) require log attrs, and thus can only be handled by
154/// `shape_item()`.
155///
156/// Note that the extra attributes in the @analyis that is returned from
157/// [`itemize()`][crate::itemize()] have indices that are relative to the entire paragraph,
158/// so you do not pass the full paragraph text as @paragraph_text, you need
159/// to subtract the item offset from their indices before calling
160/// [`shape_with_flags()`][crate::shape_with_flags()].
161/// ## `item_text`
162/// valid UTF-8 text to shape
163/// ## `item_length`
164/// the length (in bytes) of @item_text.
165/// -1 means nul-terminated text.
166/// ## `paragraph_text`
167/// text of the paragraph (see details).
168/// ## `paragraph_length`
169/// the length (in bytes) of @paragraph_text.
170/// -1 means nul-terminated text.
171/// ## `analysis`
172/// [`Analysis`][crate::Analysis] structure from [`itemize()`][crate::itemize()]
173/// ## `flags`
174/// flags influencing the shaping process
175///
176/// # Returns
177///
178///
179/// ## `glyphs`
180/// glyph string in which to store results
181#[cfg(feature = "v1_44")]
182#[cfg_attr(docsrs, doc(cfg(feature = "v1_44")))]
183#[doc(alias = "pango_shape_with_flags")]
184pub fn shape_with_flags(
185 item_text: &str,
186 paragraph_text: Option<&str>,
187 analysis: &Analysis,
188 glyphs: &mut GlyphString,
189 flags: ShapeFlags,
190) {
191 let item_length = item_text.len() as i32;
192 let paragraph_length = paragraph_text.map(|t| t.len() as i32).unwrap_or_default();
193 let paragraph_ptr = paragraph_text.map_or(ptr::null(), |t| t.as_ptr() as *const c_char);
194 unsafe {
195 // See: shape_full
196 ffi::pango_shape_with_flags(
197 item_text.as_ptr() as *const c_char,
198 item_length,
199 paragraph_ptr,
200 paragraph_length,
201 analysis.to_glib_none().0,
202 glyphs.to_glib_none_mut().0,
203 flags.into_glib(),
204 );
205 }
206}
207
208/// Converts extents from Pango units to device units.
209///
210/// The conversion is done by dividing by the `PANGO_SCALE` factor and
211/// performing rounding.
212///
213/// The @inclusive rectangle is converted by flooring the x/y coordinates
214/// and extending width/height, such that the final rectangle completely
215/// includes the original rectangle.
216///
217/// The @nearest rectangle is converted by rounding the coordinates
218/// of the rectangle to the nearest device unit (pixel).
219///
220/// The rule to which argument to use is: if you want the resulting device-space
221/// rectangle to completely contain the original rectangle, pass it in as
222/// @inclusive. If you want two touching-but-not-overlapping rectangles stay
223/// touching-but-not-overlapping after rounding to device units, pass them in
224/// as @nearest.
225///
226/// # Returns
227///
228///
229/// ## `inclusive`
230/// rectangle to round to pixels inclusively
231///
232/// ## `nearest`
233/// rectangle to round to nearest pixels
234#[doc(alias = "pango_extents_to_pixels")]
235pub fn extents_to_pixels(
236 mut inclusive: Option<&mut crate::Rectangle>,
237 mut nearest: Option<&mut crate::Rectangle>,
238) {
239 unsafe {
240 ffi::pango_extents_to_pixels(inclusive.to_glib_none_mut().0, nearest.to_glib_none_mut().0);
241 }
242}
243
244/// Breaks a piece of text into segments with consistent directional
245/// level and font.
246///
247/// Each byte of @text will be contained in exactly one of the items in the
248/// returned list; the generated list of items will be in logical order (the
249/// start offsets of the items are ascending).
250///
251/// @cached_iter should be an iterator over @attrs currently positioned
252/// at a range before or containing @start_index; @cached_iter will be
253/// advanced to the range covering the position just after
254/// @start_index + @length. (i.e. if itemizing in a loop, just keep passing
255/// in the same @cached_iter).
256/// ## `context`
257/// a structure holding information that affects
258/// the itemization process.
259/// ## `text`
260/// the text to itemize. Must be valid UTF-8
261/// ## `start_index`
262/// first byte in @text to process
263/// ## `length`
264/// the number of bytes (not characters) to process
265/// after @start_index. This must be >= 0.
266/// ## `attrs`
267/// the set of attributes that apply to @text.
268/// ## `cached_iter`
269/// Cached attribute iterator
270///
271/// # Returns
272///
273/// a `GList` of
274/// [`Item`][crate::Item] structures. The items should be freed using
275/// `Pango::Item::free()` in combination with `GLib::List::free_full()`.
276#[doc(alias = "pango_itemize")]
277pub fn itemize(
278 context: &Context,
279 text: &str,
280 start_index: i32,
281 length: i32,
282 attrs: &AttrList,
283 cached_iter: Option<&AttrIterator>,
284) -> Vec<Item> {
285 let total_length = text.len() as i32;
286 assert!(
287 start_index >= 0 && start_index < total_length,
288 "start_index is out of range"
289 );
290 assert!(
291 length >= 0 && start_index.checked_add(length).unwrap() <= total_length,
292 "start_index + length is out of range"
293 );
294 unsafe {
295 FromGlibPtrContainer::from_glib_full(ffi::pango_itemize(
296 context.to_glib_none().0,
297 text.to_glib_none().0,
298 start_index,
299 length,
300 attrs.to_glib_none().0,
301 mut_override(cached_iter.to_glib_none().0),
302 ))
303 }
304}
305
306/// Like `pango_itemize()`, but with an explicitly specified base direction.
307///
308/// The base direction is used when computing bidirectional levels.
309/// [`itemize()`][crate::itemize()] gets the base direction from the [`Context`][crate::Context]
310/// (see [`Context::set_base_dir()`][crate::Context::set_base_dir()]).
311/// ## `context`
312/// a structure holding information that affects
313/// the itemization process.
314/// ## `base_dir`
315/// base direction to use for bidirectional processing
316/// ## `text`
317/// the text to itemize.
318/// ## `start_index`
319/// first byte in @text to process
320/// ## `length`
321/// the number of bytes (not characters) to process
322/// after @start_index. This must be >= 0.
323/// ## `attrs`
324/// the set of attributes that apply to @text.
325/// ## `cached_iter`
326/// Cached attribute iterator
327///
328/// # Returns
329///
330/// a `GList` of
331/// [`Item`][crate::Item] structures. The items should be freed using
332/// `Pango::Item::free()` probably in combination with `GLib::List::free_full()`.
333#[doc(alias = "pango_itemize_with_base_dir")]
334pub fn itemize_with_base_dir(
335 context: &Context,
336 base_dir: Direction,
337 text: &str,
338 start_index: i32,
339 length: i32,
340 attrs: &AttrList,
341 cached_iter: Option<&AttrIterator>,
342) -> Vec<Item> {
343 let total_length = text.len() as i32;
344 assert!(
345 start_index >= 0 && start_index < total_length,
346 "start_index is out of range"
347 );
348 assert!(
349 length >= 0 && start_index.checked_add(length).unwrap() <= total_length,
350 "start_index + length is out of range"
351 );
352 unsafe {
353 FromGlibPtrContainer::from_glib_full(ffi::pango_itemize_with_base_dir(
354 context.to_glib_none().0,
355 base_dir.into_glib(),
356 text.to_glib_none().0,
357 start_index,
358 length,
359 attrs.to_glib_none().0,
360 mut_override(cached_iter.to_glib_none().0),
361 ))
362 }
363}