Skip to main content

cat_engine/text/
mod.rs

1//! # Рендеринг текста. Text rendering. `feature = "text_graphics"`, `default_features`
2//! 
3//! Как рендерятся символы:
4//! 1. Первый вариант - с помощью обычных шрифтов:
5//!    - 
6//!     1. Создаётся контур символа
7//!     2. Этот символ записывается в массив как изображение
8//!     3. Изображение загружается в текстуру
9//!     4. Текстура выводится на экран
10//! 2. Второй вариант - с помощью хранилищ глифов
11//!    - 
12//!     1. Глиф достаётся из хранилища
13//!     2. Он масштабируется
14//!     3. Его текстура выводится на экран
15//! ###
16//! 
17//! How characters are rendering:
18//! 1. The first way - with common fonts:
19//!    -
20//!     1. Building glyph's outline
21//!     2. Converting the glyph to an image
22//!     3. Loading the image a texture
23//!     4. Rendering the texture
24//! 2. The second way - with glyph caches
25//!    -
26//!     1. Taking a glyph from a glyph cache
27//!     2. Scaling the glyph
28//!     3. Rendering it's texture
29//! 
30//! ### A simple example
31//! ```
32//! let mut window=PagedWindow::new(|_,s|{
33//!     s.vsync=true;
34//!     // Max size for glyph images
35//!     s.graphics_base_settings.text.glyph_texture_size=[500,500];
36//! }).unwrap();
37//! 
38//! let font=FontOwner::load("resources/font").unwrap();
39//! let wfont=font.face_wrapper();
40//! 
41//! ... in the cycle:
42//!     let base=TextBase::new([300f32,400f32],Scale::new(0.1,0.1),[1f32;4]);
43//!     base.draw_str("HelloWorld$?",&wfont,p,g);
44//! 
45//! ```
46//! 
47//! ### A glyph cache example
48//! ```
49//! let mut window=PagedWindow::new(|_,s|{
50//!     s.vsync=true;
51//! }).unwrap();
52//! 
53//! let font=FontOwner::load("resources/font").unwrap();
54//! 
55//! let scale=Scale::new(0.4,0.4);
56//! // Creating a new glyph cache for the given characters
57//! let glyphs=GlyphCache::new_alphabet(&font,"HelloWorld?",scale,window.display());
58//! 
59//! ... in the cycle:
60//!     let base=TextBase::new([300f32,400f32],Scale::new(0.1,0.1),[1f32;4]);
61//!     base.draw_str_glyph_cache("HelloWorld?",&glyphs,p,g);
62//! 
63//! ```
64
65// Определения \\
66
67// Глиф (glyph) - здесь изображение (текстура) символа.
68
69// Ascender (выносной элемент) - в типографике часть строчной буквы,
70// выходящая за пределы линии строчных знаков или базовой линии шрифта.
71// Здесь расстояние от строки до верхней границы этой части.
72// Примеры: загагулина у буквы f, палочка у букв h и b, крышка у буквы А.
73
74// Размер шрифта (font size) - здесь высота,
75// под которую выравниваются все текстуры символов.
76// Таким будет размер самого большого глифа при рендеринге.
77// Но определить точный размер этого глифа не всегда удобно, поэтому
78// чаще всего все символы будут чуть меньше. Используйте функцию `text_size`
79// для точного определения точных размеров текста.
80
81// Хранилище \\
82
83// Все символы хранятся в вместе с глифами в хранилище (`GlyphCache`).
84// Для каждого символа создаётся текстура и в неё загружается глиф.
85// Поиск глифов по символам выполняется с помощью функций `HashMap`.
86
87use crate::{
88    // types
89    Colour,
90    // structs
91    graphics::Graphics,
92};
93
94#[cfg(feature="colour_filter")]
95use crate::graphics::ColourFilter;
96
97mod glyph;
98pub use glyph::*;
99
100mod outline;
101pub (crate) use outline::{
102    OutlineCurve,
103    OutlineCurveBuilder,
104};
105
106pub use outline::{
107    Scale,
108};
109
110mod glyph_cache;
111pub use glyph_cache::{
112    GlyphCache,
113    RawGlyphCache,
114};
115
116mod font;
117pub use font::{
118    FontOwner,
119    FaceWrapper,
120    CachedFont,
121    Font,
122};
123
124
125use glium::{Surface,DrawError};
126
127// re-export
128pub use ttf_parser;
129pub use ab_glyph_rasterizer;
130
131/// Основа для рендеринга текста.
132/// 
133/// A base for text rendering.
134pub struct TextBase{
135    pub position:[f32;2],
136    pub scale:Scale,
137    pub colour:Colour,
138}
139
140impl TextBase{
141    pub const fn new(position:[f32;2],scale:Scale,colour:Colour)->TextBase{
142        Self{
143            scale,
144            colour,
145            position,
146        }
147    }
148
149    pub const fn zero_position(scale:Scale,colour:Colour)->TextBase{
150        Self{
151            scale,
152            colour,
153            position:[0f32;2],
154        }
155    }
156
157    #[inline(always)]
158    pub fn set_x(&mut self,x:f32){
159        self.position[0]=x
160    }
161
162    #[inline(always)]
163    pub fn set_y(&mut self,y:f32){
164        self.position[1]=y
165    }
166
167    #[inline(always)]
168    pub fn move_to(&mut self,position:[f32;2]){
169        self.position=position
170    }
171
172    #[inline(always)]
173    pub fn shift_x(&mut self,dx:f32){
174        self.position[0]+=dx
175    }
176
177    #[inline(always)]
178    pub fn shift_y(&mut self,dy:f32){
179        self.position[1]+=dy
180    }
181
182    #[inline(always)]
183    pub fn shift(&mut self,dx:f32,dy:f32){
184        self.position[0]+=dx;
185        self.position[1]+=dy;
186    }
187
188    #[inline(always)]
189    pub fn set_alpha_channel(&mut self,alpha:f32){
190        self.colour[3]=alpha
191    }
192
193    #[inline(always)]
194    pub fn set_colour(&mut self,colour:Colour){
195        self.colour=colour
196    }
197}
198
199
200
201
202
203impl TextBase{
204    /// Выводит символ.
205    /// 
206    /// Draws a character.
207    #[inline(always)]
208    pub fn draw_char<F:Font,S:Surface>(
209        &self,
210        character:char,
211        font:&F,
212        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
213        graphics:&mut Graphics<S>
214    )->Result<(),DrawError>{
215        let glyph=if let Some(glyph)=font.build_raw_glyph(character){
216            glyph
217        }
218        else{
219            if character.is_whitespace(){
220                return Ok(())
221            }
222            else{
223                font.build_raw_undefined_glyph()
224            }
225        };
226
227        // Глиф для рендеринга
228        let outlined=glyph.outlined_glyph(self.scale);
229
230        // Позиция для глифа для рендеринга
231        let position={
232            let size=outlined.size();
233            let offset=outlined.offset();
234            [
235                self.position[0],
236                self.position[1]-offset[1]-size[1] as f32,
237            ]
238        };
239
240        graphics.draw_glyph(
241            &outlined,
242            self.colour,
243            position,
244            #[cfg(feature="colour_filter")]colour_filter,
245        )
246    }
247
248    /// Выводит сдвинутый символ.
249    /// 
250    /// Draws a shifted character.
251    #[inline(always)]
252    pub fn draw_shift_char<F:Font,S:Surface>(
253        &self,
254        character:char,
255        shift:[f32;2],
256        font:&F,
257        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
258        graphics:&mut Graphics<S>
259    )->Result<(),DrawError>{
260        let glyph=if let Some(glyph)=font.build_raw_glyph(character){
261            glyph
262        }
263        else{
264            if character.is_whitespace(){
265                return Ok(())
266            }
267            else{
268                font.build_raw_undefined_glyph()
269            }
270        };
271
272        // Глиф для рендеринга
273        let outlined=glyph.outlined_glyph(self.scale);
274
275        // Позиция для глифа для рендеринга
276        let position={
277            let size=outlined.size();
278            let offset=outlined.offset();
279            [
280                self.position[0],
281                self.position[1]-offset[1]-size[1] as f32,
282            ]
283        };
284
285        graphics.draw_shift_glyph(
286            &outlined,
287            self.colour,
288            position,
289            shift,
290            #[cfg(feature="colour_filter")]colour_filter,
291        )
292    }
293
294    /// Выводит повёрнутый символ.
295    /// 
296    /// Draws a rotated character.
297    #[inline(always)]
298    pub fn draw_rotate_char<F:Font,S:Surface>(
299        &self,
300        character:char,
301        rotation_center:[f32;2],
302        angle:f32,
303        font:&F,
304        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
305        graphics:&mut Graphics<S>
306    )->Result<(),DrawError>{
307        let glyph=if let Some(glyph)=font.build_raw_glyph(character){
308            glyph
309        }
310        else{
311            if character.is_whitespace(){
312                return Ok(())
313            }
314            else{
315                font.build_raw_undefined_glyph()
316            }
317        };
318
319        // Глиф для рендеринга
320        let outlined=glyph.outlined_glyph(self.scale);
321
322        // Позиция для глифа для рендеринга
323        let position={
324            let size=outlined.size();
325            let offset=outlined.offset();
326            [
327                self.position[0],
328                self.position[1]-offset[1]-size[1] as f32,
329            ]
330        };
331
332        graphics.draw_rotate_glyph(
333            &outlined,
334            self.colour,
335            position,
336            rotation_center,
337            angle,
338            #[cfg(feature="colour_filter")]colour_filter
339        )
340    }
341
342    /// Выводит строку.
343    /// 
344    /// Draws a string.
345    pub fn draw_str<F:Font,S:Surface>(
346        &self,
347        s:&str,
348        font:&F,
349        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
350        graphics:&mut Graphics<S>
351    )->Result<(),DrawError>{
352        let mut position=self.position;
353
354        let mut glyph; // Немасштабированный глиф
355
356        // Расстояние до следующего глифа для пробела
357        let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
358
359        for character in s.chars(){
360            glyph=if let Some(glyph)=font.build_raw_glyph(character){
361                glyph
362            }
363            else{
364                if character==' '{
365                    position[0]+=whitespace_advance;
366                    continue
367                }
368
369                font.build_raw_undefined_glyph()
370            };
371
372            // Масштабированный глиф
373            let scaled=glyph.scale(self.scale);
374
375            let rect=scaled.positioned_bounding_box(position);
376
377            // Расстояние до следующего глифа по горизонтали
378            let advance_width=scaled.advance_width();
379
380            // Глиф для рендеринга
381            let outlined=scaled.outline();
382
383            graphics.draw_glyph(
384                &outlined,
385                self.colour,
386                [rect[0],rect[1]],
387                #[cfg(feature="colour_filter")]colour_filter,
388            )?;
389
390            position[0]+=advance_width;
391        }
392
393        Ok(())
394    }
395
396    /// Выводит сдвинутую строку.
397    /// 
398    /// Draws a shifted string.
399    pub fn draw_shift_str<F:Font,S:Surface>(
400        &self,
401        s:&str,
402        shift:[f32;2],
403        font:&F,
404        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
405        graphics:&mut Graphics<S>
406    )->Result<(),DrawError>{
407        let mut position=self.position;
408
409        let mut glyph; // Немасштабированный глиф
410
411        // Расстояние до следующего глифа для пробела
412        let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
413
414        for character in s.chars(){
415            glyph=if let Some(glyph)=font.build_raw_glyph(character){
416                glyph
417            }
418            else{
419                if character==' '{
420                    position[0]+=whitespace_advance;
421                    continue
422                }
423
424                font.build_raw_undefined_glyph()
425            };
426
427            // Масштабированный глиф
428            let scaled=glyph.scale(self.scale);
429
430            let rect=scaled.positioned_bounding_box(position);
431
432            // Расстояние до следующего глифа по горизонтали
433            let advance_width=scaled.advance_width();
434
435            // Глиф для рендеринга
436            let outlined=scaled.outline();
437
438            graphics.draw_shift_glyph(
439                &outlined,
440                self.colour,
441                [rect[0],rect[1]],
442                shift,
443                #[cfg(feature="colour_filter")]colour_filter,
444            )?;
445
446            position[0]+=advance_width;
447        }
448
449        Ok(())
450    }
451
452    /// Выводит повёрнутую строку.
453    /// 
454    /// Draws a rotated string.
455    pub fn draw_rotate_str<F:Font,S:Surface>(
456        &self,
457        s:&str,
458        rotation_center:[f32;2],
459        angle:f32,
460        font:&F,
461        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
462        graphics:&mut Graphics<S>
463    )->Result<(),DrawError>{
464        let mut position=self.position;
465
466        let mut glyph; // Немасштабированный глиф
467
468        // Расстояние до следующего глифа для пробела
469        let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
470
471        for character in s.chars(){
472            glyph=if let Some(glyph)=font.build_raw_glyph(character){
473                glyph
474            }
475            else{
476                if character==' '{
477                    position[0]+=whitespace_advance;
478                    continue
479                }
480
481                font.build_raw_undefined_glyph()
482            };
483
484            // Масштабированный глиф
485            let scaled=glyph.scale(self.scale);
486
487            let rect=scaled.positioned_bounding_box(position);
488
489            // Расстояние до следующего глифа
490            let advance_width=scaled.advance_width();
491
492            // Глиф для рендеринга
493            let outlined=scaled.outline();
494
495            graphics.draw_rotate_glyph(
496                &outlined,
497                self.colour,
498                [rect[0],rect[1]],
499                rotation_center,
500                angle,
501                #[cfg(feature="colour_filter")]colour_filter
502            )?;
503
504            position[0]+=advance_width;
505        }
506
507        Ok(())
508    }
509}
510
511
512
513
514
515impl TextBase{
516    /// Строит и выводит один символ.
517    /// 
518    /// Берёт соответствующий глиф из данного хранилища.
519    /// 
520    /// Builds and draws a character.
521    /// 
522    /// Takes a corresponding glyph from the given cache.
523    #[inline(always)]
524    pub fn draw_char_glyph_cache<C:RawGlyphCache,S:Surface>(
525        &self,
526        character:char,
527        glyph_cache:&C,
528        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
529        graphics:&mut Graphics<S>
530    )->Result<(),DrawError>{
531        let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
532            glyph
533        }
534        else{
535            if character.is_whitespace(){
536                return Ok(())
537            }
538            else{
539                glyph_cache.scaled_undefined_glyph(self.scale)
540            }
541        };
542
543        let rect=glyph.positioned_bounding_box(self.position);
544
545        // Создание глифа для рендеринга
546        let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
547
548        graphics.draw_glyph_cache(
549            &textured,
550            self.colour,
551            [rect[0],rect[1]],
552            #[cfg(feature="colour_filter")]colour_filter
553        )
554    }
555
556    /// Выводит сдвинутый символ.
557    /// 
558    /// Берёт соответствующий глиф из данного хранилища.
559    /// 
560    /// Draws a shifted character.
561    /// 
562    /// Takes a corresponding glyph from the given cache.
563    #[inline(always)]
564    pub fn draw_shift_char_glyph_cache<C:RawGlyphCache,S:Surface>(
565        &self,
566        character:char,
567        shift:[f32;2],
568        glyph_cache:&C,
569        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
570        graphics:&mut Graphics<S>
571    )->Result<(),DrawError>{
572        let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
573            glyph
574        }
575        else{
576            if character.is_whitespace(){
577                return Ok(())
578            }
579            else{
580                glyph_cache.scaled_undefined_glyph(self.scale)
581            }
582        };
583
584        let rect=glyph.positioned_bounding_box(self.position);
585
586        // Создание глифа для рендеринга
587        let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
588
589        graphics.draw_shift_glyph_cache(
590            &textured,
591            self.colour,
592            [rect[0],rect[1]],
593            shift,
594            #[cfg(feature="colour_filter")]colour_filter
595        )
596    }
597
598    /// Выводит символ.
599    /// 
600    /// Берёт соответствующий глиф из данного хранилища.
601    /// 
602    /// Draws a character.
603    /// 
604    /// Takes a corresponding glyph from the given cache.
605    #[inline(always)]
606    pub fn draw_rotate_char_glyph_cache<C:RawGlyphCache,S:Surface>(
607        &self,
608        character:char,
609        rotation_center:[f32;2],
610        angle:f32,
611        glyph_cache:&C,
612        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
613        graphics:&mut Graphics<S>
614    )->Result<(),DrawError>{
615        let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
616            glyph
617        }
618        else{
619            if character.is_whitespace(){
620                return Ok(())
621            }
622            else{
623                glyph_cache.scaled_undefined_glyph(self.scale)
624            }
625        };
626
627        let rect=glyph.positioned_bounding_box(self.position);
628
629        // Создание глифа для рендеринга
630        let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
631
632        graphics.draw_rotate_glyph_cache(
633            &textured,
634            self.colour,
635            [rect[0],rect[1]],
636            rotation_center,
637            angle,
638            #[cfg(feature="colour_filter")]colour_filter
639        )
640    }
641
642    /// Выводит строку.
643    /// 
644    /// Берёт соответствующие глифы из данного хранилища.
645    /// 
646    /// Draws a string.
647    /// 
648    /// Takes corresponding glyphs from the given cache.
649    pub fn draw_str_glyph_cache<C:RawGlyphCache,S:Surface>(
650        &self,
651        s:&str,
652        glyph_cache:&C,
653        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
654        graphics:&mut Graphics<S>
655    )->Result<(),DrawError>{
656        let mut position=self.position;
657
658        let mut glyph;
659
660        for character in s.chars(){
661            glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
662                glyph
663            }
664            else{
665                if character==' '{
666                    position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
667                    continue
668                }
669
670                glyph_cache.scaled_undefined_glyph(self.scale)
671            };
672
673            let rect=glyph.positioned_bounding_box(position);
674
675            // Создание глифа для рендеринга
676            let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
677
678            graphics.draw_glyph_cache(
679                &textured,
680                self.colour,
681                [rect[0],rect[1]],
682                #[cfg(feature="colour_filter")]colour_filter
683            )?;
684
685            position[0]+=glyph.advance_width();
686        }
687
688        Ok(())
689    }
690
691    /// Выводит сдвинутую строку.
692    /// 
693    /// Берёт соответствующие глифы из данного хранилища.
694    /// 
695    /// Draws a shifted string.
696    /// 
697    /// Takes corresponding glyphs from the given cache.
698    pub fn draw_shift_str_glyph_cache<C:RawGlyphCache,S:Surface>(
699        &self,
700        s:&str,
701        shift:[f32;2],
702        glyph_cache:&C,
703        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
704        graphics:&mut Graphics<S>
705    )->Result<(),DrawError>{
706        let mut position=self.position;
707
708        let mut glyph; // Мастабированный глиф
709
710        for character in s.chars(){
711            glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
712                glyph
713            }
714            else{
715                if character==' '{
716                    position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
717                    continue
718                }
719
720                glyph_cache.scaled_undefined_glyph(self.scale)
721            };
722
723            let rect=glyph.positioned_bounding_box(position);
724
725            // Создание глифа для рендеринга
726            let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
727
728            graphics.draw_shift_glyph_cache(
729                &textured,
730                self.colour,
731                [rect[0],rect[1]],
732                shift,
733                #[cfg(feature="colour_filter")]colour_filter
734            )?;
735
736            position[0]+=glyph.advance_width();
737        }
738
739        Ok(())
740    }
741
742    
743
744    /// Выводит повёрнутую строку.
745    /// 
746    /// Берёт соответствующие глифы из данного хранилища.
747    /// 
748    /// Draws a rotated string.
749    /// 
750    /// Takes corresponding glyphs from the given cache.
751    pub fn draw_rotate_str_glyph_cache<C:RawGlyphCache,S:Surface>(
752        &self,
753        s:&str,
754        rotation_center:[f32;2],
755        angle:f32,
756        glyph_cache:&C,
757        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
758        graphics:&mut Graphics<S>
759    )->Result<(),DrawError>{
760        let mut position=self.position;
761
762        let mut glyph; // Мастабированный глиф
763
764        for character in s.chars(){
765            glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
766                glyph
767            }
768            else{
769                if character==' '{
770                    position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
771                    continue
772                }
773
774                glyph_cache.scaled_undefined_glyph(self.scale)
775            };
776
777            let rect=glyph.positioned_bounding_box(position);
778
779            // Создание глифа для рендеринга
780            let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
781
782            graphics.draw_rotate_glyph_cache(
783                &textured,
784                self.colour,
785                [rect[0],rect[1]],
786                rotation_center,
787                angle,
788                #[cfg(feature="colour_filter")]colour_filter
789            )?;
790
791            position[0]+=glyph.advance_width();
792        }
793
794        Ok(())
795    }
796
797    /// Выводит часть строки.
798    /// Если текст выведен полностью, возвращает `true`.
799    /// 
800    /// Берёт соответствующие глифы из данного хранилища.
801    /// 
802    /// Draws a part of a string.
803    /// Returns `true`, if the whole string is drawn.
804    /// 
805    /// Takes corresponding glyphs from the given cache.
806    pub fn draw_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
807        &self,
808        s:&str,
809        chars:usize,
810        glyph_cache:&C,
811        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
812        graphics:&mut Graphics<S>
813    )->Result<bool,DrawError>{
814        let mut whole=true; // Флаг вывода всего текста
815
816        let mut position=self.position;
817
818        let mut glyph; // Мастабированный глиф
819
820        for (i,character) in s.chars().enumerate(){
821            // Выход из цикла при достижении лимита символов
822            if i==chars{
823                whole=false;
824                break
825            }
826
827            glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
828                glyph
829            }
830            else{
831                if character==' '{
832                    position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
833                    continue
834                }
835
836                glyph_cache.scaled_undefined_glyph(self.scale)
837            };
838
839            let rect=glyph.positioned_bounding_box(position);
840
841            // Создание глифа для рендеринга
842            let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
843
844            graphics.draw_glyph_cache(
845                &textured,
846                self.colour,
847                [rect[0],rect[1]],
848                #[cfg(feature="colour_filter")]colour_filter
849            )?;
850
851            position[0]+=glyph.advance_width();
852        }
853
854        Ok(whole)
855    }
856
857    /// Выводит часть строки.
858    /// Если текст выведен полностью, возвращает `true`.
859    /// 
860    /// Берёт соответствующие глифы из данного хранилища.
861    /// 
862    /// Draws a part of a string.
863    /// Returns `true`, if the whole string is drawn.
864    /// 
865    /// Takes corresponding glyphs from the given cache.
866    pub fn draw_shift_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
867        &self,
868        s:&str,
869        chars:usize,
870        shift:[f32;2],
871        glyph_cache:&C,
872        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
873        graphics:&mut Graphics<S>
874    )->Result<bool,DrawError>{
875        let mut whole=true; // Флаг вывода всего текста
876
877        let mut position=self.position;
878
879        let mut glyph; // Мастабированный глиф
880
881        for (i,character) in s.chars().enumerate(){
882            // Выход из цикла при достижении лимита символов
883            if i==chars{
884                whole=false;
885                break
886            }
887
888            glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
889                glyph
890            }
891            else{
892                if character==' '{
893                    position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
894                    continue
895                }
896
897                glyph_cache.scaled_undefined_glyph(self.scale)
898            };
899
900            let rect=glyph.positioned_bounding_box(position);
901
902            // Создание глифа для рендеринга
903            let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
904
905            graphics.draw_shift_glyph_cache(
906                &textured,
907                self.colour,
908                [rect[0],rect[1]],
909                shift,
910                #[cfg(feature="colour_filter")]colour_filter
911            )?;
912
913            position[0]+=glyph.advance_width();
914        }
915
916        Ok(whole)
917    }
918
919    /// Выводит часть повёрнутой строки.
920    /// Если текст выведен полностью, возвращает true.
921    /// 
922    /// Берёт соответствующие глифы из данного хранилища.
923    /// 
924    /// Draws a part of a rotated string.
925    /// Returns true, if the whole string is drawn.
926    /// 
927    /// Takes corresponding glyphs from the given cache.
928    pub fn draw_rotate_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
929        &self,
930        s:&str,
931        chars:usize,
932        rotation_center:[f32;2],
933        angle:f32,
934        glyph_cache:&C,
935        #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
936        graphics:&mut Graphics<S>
937    )->Result<bool,DrawError>{
938        let mut whole=true; // Флаг вывода всего текста
939
940        let mut position=self.position;
941
942        let mut glyph;
943
944        for (i,character) in s.chars().enumerate(){
945            // Выход из цикла при достижении лимита символов
946            if i==chars{
947                whole=false;
948                break
949            }
950
951            glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
952                glyph
953            }
954            else{
955                if character==' '{
956                    position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
957                    continue
958                }
959
960                glyph_cache.scaled_undefined_glyph(self.scale)
961            };
962
963            let rect=glyph.positioned_bounding_box(position);
964
965            // Создание глифа для рендеринга
966            let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
967
968            graphics.draw_rotate_glyph_cache(
969                &textured,
970                self.colour,
971                [rect[0],rect[1]],
972                rotation_center,
973                angle,
974                #[cfg(feature="colour_filter")]colour_filter
975            )?;
976
977            position[0]+=glyph.advance_width();
978        }
979
980        Ok(whole)
981    }
982}