1use std::path::Path;
38use std::sync::Arc;
39
40use glyphon::fontdb::Source;
41use glyphon::{
42    self, Attrs, Family, FontSystem, Metrics, Shaping, SwashCache, TextArea, TextAtlas, TextBounds,
43};
44
45use crate::colour::Colour;
46use crate::context::WgpuClump;
47use crate::engine_handle::Engine;
48use crate::material::{self, Material};
49use crate::matrix_math::normalize_points;
50use crate::render::Renderer;
51use crate::resource::{
52    self, InProgressResource, LoadingOp, ResourceId, ResourceManager, ResourceType,
53};
54use crate::vectors::Vec2;
55use crate::vertex::{self, Vertex};
56use crate::{layouts, vec2};
57
58pub(crate) struct TextRenderer {
61    pub(crate) font_system: FontSystem,
62    swash_cache: SwashCache,
63    text_cache: glyphon::Cache,
64    atlas: TextAtlas,
65    text_renderer: glyphon::TextRenderer,
66    defualt_font_name: String,
67}
68
69impl TextRenderer {
70    pub fn new(wgpu: &WgpuClump) -> Self {
71        let mut font_system = FontSystem::new();
72        let defualt_font_id = font_system
73            .db_mut()
74            .load_font_source(Source::Binary(Arc::new(include_bytes!("Monocraft.ttf"))))[0];
75
76        let defualt_font_name = font_system.db().face(defualt_font_id).unwrap().families[0]
77            .0
78            .clone();
79        let text_cache = glyphon::Cache::new(&wgpu.device);
82
83        let swash_cache = SwashCache::new();
84        let mut atlas = TextAtlas::new(
85            &wgpu.device,
86            &wgpu.queue,
87            &text_cache,
88            wgpu::TextureFormat::Rgba8UnormSrgb,
89        );
90        let text_renderer = glyphon::TextRenderer::new(
91            &mut atlas,
92            &wgpu.device,
93            wgpu::MultisampleState::default(),
94            None,
95        );
96
97        Self {
98            font_system,
99            swash_cache,
100            text_cache,
101            atlas,
102            text_renderer,
103            defualt_font_name,
104        }
105    }
106
107    pub fn get_defualt_font_name(&self) -> &str {
108        &self.defualt_font_name
109    }
110
111    pub fn load_font_from_bytes(&mut self, data: &[u8]) -> Font {
113        let id = self
114            .font_system
115            .db_mut()
116            .load_font_source(Source::Binary(Arc::new(data.to_vec())))[0];
117        let name = self.font_system.db().face(id).unwrap().families[0]
120            .0
121            .clone();
122
123        Font { name }
124    }
125}
126
127pub struct TextMaterial {
131    font_size: f32,
132    line_height: f32,
133    text: String,
134    inner: Option<InnerMaterial>,
135    colour: Colour,
136    vertex_count: u64,
137    index_count: u64,
138}
139
140impl TextMaterial {
141    pub fn new(text: &str, colour: Colour, font_size: f32, line_height: f32) -> Self {
142        Self {
143            font_size,
144            line_height,
145            text: text.into(),
146            colour,
147            inner: None,
148            vertex_count: 0,
149            index_count: 0,
150        }
151    }
152
153    pub fn set_text(&mut self, text: &str, colour: Colour, engine: &mut Engine) {
156        self.text = text.into();
157
158        let context = match &mut engine.context {
160            None => return,
161            Some(c) => c,
162        };
163
164        if self.add_inner(
165            &context.wgpu,
166            &mut context.text_renderer,
167            &engine.resource_manager,
168            &context.texture_sampler,
169        ) {
170            return;
171        }
172
173        let inner = self.inner.as_mut().unwrap();
174        let font_info = FontInformation {
175            wgpu: &context.wgpu,
176            text_handle: &mut context.text_renderer,
177            resources: &engine.resource_manager,
178        };
179
180        inner.text_buffer.set_text(
181            &mut font_info.text_handle.font_system,
182            text,
183            Attrs::new()
184                .color(colour.into())
185                .family(Family::Name(&font_info.text_handle.defualt_font_name)),
186            Shaping::Basic,
187        );
188
189        self.update_measurements(font_info);
190    }
191
192    pub fn set_text_with_font(
195        &mut self,
196        text: &str,
197        colour: Colour,
198        font: &ResourceId<Font>,
199        engine: &mut Engine,
200    ) {
201        self.text = text.into();
202
203        let context = match &mut engine.context {
205            None => return,
206            Some(c) => c,
207        };
208
209        self.add_inner(
210            &context.wgpu,
211            &mut context.text_renderer,
212            &engine.resource_manager,
213            &context.texture_sampler,
214        );
215
216        let inner = self.inner.as_mut().unwrap();
217        let font_info = FontInformation {
218            wgpu: &context.wgpu,
219            text_handle: &mut context.text_renderer,
220            resources: &engine.resource_manager,
221        };
222
223        let backup = &String::new();
224        let name = font_info
225            .resources
226            .get_font(font)
227            .map(|f| &f.name)
228            .unwrap_or(backup);
229
230        inner.text_buffer.set_text(
231            &mut font_info.text_handle.font_system,
232            text,
233            Attrs::new().color(colour.into()).family(Family::Name(name)),
234            Shaping::Basic,
235        );
236
237        self.update_measurements(font_info);
238    }
239
240    pub fn set_bounds(&mut self, position: Vec2<i32>, size: Vec2<i32>) {
242        if let Some(inner) = &mut self.inner {
243            inner.bounds = Vec2 {
244                x: position,
245                y: size,
246            }
247        }
248    }
249
250    pub fn set_font_size(&mut self, new_size: f32, engine: &mut Engine) {
252        self.font_size = new_size;
253        let context = match &mut engine.context {
255            None => return,
256            Some(c) => c,
257        };
258
259        if self.add_inner(
260            &context.wgpu,
261            &mut context.text_renderer,
262            &engine.resource_manager,
263            &context.texture_sampler,
264        ) {
265            return;
266        }
267
268        let inner = self.inner.as_mut().unwrap();
269        let font_info = FontInformation {
270            wgpu: &context.wgpu,
271            text_handle: &mut context.text_renderer,
272            resources: &engine.resource_manager,
273        };
274
275        let metrics = Metrics::new(self.font_size, self.line_height);
276        inner
277            .text_buffer
278            .set_metrics(&mut font_info.text_handle.font_system, metrics);
279
280        self.update_measurements(font_info);
281    }
282
283    pub fn set_line_height(&mut self, new_height: f32, engine: &mut Engine) {
285        self.line_height = new_height;
286        let context = match &mut engine.context {
288            None => return,
289            Some(c) => c,
290        };
291
292        if self.add_inner(
293            &context.wgpu,
294            &mut context.text_renderer,
295            &engine.resource_manager,
296            &context.texture_sampler,
297        ) {
298            return;
299        }
300
301        let inner = self.inner.as_mut().unwrap();
302        let font_info = FontInformation {
303            wgpu: &context.wgpu,
304            text_handle: &mut context.text_renderer,
305            resources: &engine.resource_manager,
306        };
307
308        let metrics = Metrics::new(self.font_size, self.line_height);
309        inner
310            .text_buffer
311            .set_metrics(&mut font_info.text_handle.font_system, metrics);
312
313        self.update_measurements(font_info);
314    }
315
316    pub fn get_measurements(&self) -> Vec2<u32> {
318        self.inner.as_ref().map(|c| c.size).unwrap_or(vec2!(0))
319    }
320
321    pub fn get_text(&self) -> &str {
322        &self.text
323    }
324
325    fn update_measurements(&mut self, font_info: FontInformation) {
326        let inner = self
327            .inner
328            .as_mut()
329            .expect("inner should be inilized before measuring");
330
331        let hieght = inner.text_buffer.lines.len() as f32 * inner.text_buffer.metrics().line_height;
332        let run_width = inner
333            .text_buffer
334            .layout_runs()
335            .map(|run| run.line_w)
336            .max_by(f32::total_cmp)
337            .unwrap_or(0.0);
338
339        inner.size = Vec2 {
340            x: run_width.ceil() as u32,
341            y: hieght.ceil() as u32,
342        };
343
344        inner.texture = font_info
345            .wgpu
346            .device
347            .create_texture(&wgpu::TextureDescriptor {
348                label: Some("Text Texture"),
349                size: wgpu::Extent3d {
350                    width: inner.size.x,
351                    height: inner.size.y,
352                    depth_or_array_layers: 1,
353                },
354                mip_level_count: 1,
355                sample_count: 1,
356                dimension: wgpu::TextureDimension::D2,
357                format: wgpu::TextureFormat::Rgba8UnormSrgb,
358                usage: wgpu::TextureUsages::RENDER_ATTACHMENT
359                    | wgpu::TextureUsages::TEXTURE_BINDING,
360                view_formats: &[],
361            });
362
363        inner
364            .viewport
365            .update(&font_info.wgpu.queue, inner.size.into());
366    }
367
368    pub fn add_instance(&mut self, position: Vec2<f32>, tint: Colour, render: &Renderer) {
371        let inner = self.inner.as_ref().unwrap();
372
373        let rect_size = Vec2 {
374            x: inner.size.x as f32,
375            y: inner.size.y as f32,
376        };
377        let wgpu = render.wgpu;
378        let verts = vertex::from_pixels(position, rect_size, tint.as_raw());
379
380        self.push_rectangle(wgpu, verts);
381    }
382
383    pub fn add_instance_with_rotation(
386        &mut self,
387        position: Vec2<f32>,
388        tint: Colour,
389        degrees: f32,
390        render: &Renderer,
391    ) {
392        let inner = self.inner.as_ref().unwrap();
393
394        let rect_size = Vec2 {
395            x: inner.size.x as f32,
396            y: inner.size.y as f32,
397        };
398        let wgpu = render.wgpu;
399        let verts = vertex::from_pixels_with_rotation(position, rect_size, tint.as_raw(), degrees);
400
401        self.push_rectangle(wgpu, verts);
402    }
403
404    pub fn add_instance_with_uv(
407        &mut self,
408        position: Vec2<f32>,
409        size: Vec2<f32>,
410        uv_pos: Vec2<f32>,
411        uv_size: Vec2<f32>,
412        tint: Colour,
413        render: &Renderer,
414    ) {
415        let inner = self.inner.as_ref().unwrap();
416
417        let wgpu = render.wgpu;
418
419        let uv_pos = normalize_points(
420            uv_pos,
421            Vec2 {
422                x: inner.size.x as f32,
423                y: inner.size.y as f32,
424            },
425        );
426        let uv_size = normalize_points(
427            uv_size,
428            Vec2 {
429                x: inner.size.x as f32,
430                y: inner.size.y as f32,
431            },
432        );
433
434        let verts = vertex::from_pixels_with_uv(position, size, tint.as_raw(), uv_pos, uv_size);
435
436        self.push_rectangle(wgpu, verts);
437    }
438
439    #[allow(clippy::too_many_arguments)]
440    pub fn add_instace_ex(
442        &mut self,
443        position: Vec2<f32>,
444        size: Vec2<f32>,
445        uv_pos: Vec2<f32>,
446        uv_size: Vec2<f32>,
447        degrees: f32,
448        tint: Colour,
449        render: &Renderer,
450    ) {
451        let inner = self.inner.as_ref().unwrap();
452
453        let wgpu = render.wgpu;
454
455        let uv_pos = normalize_points(
456            uv_pos,
457            Vec2 {
458                x: inner.size.x as f32,
459                y: inner.size.y as f32,
460            },
461        );
462        let uv_size = normalize_points(
463            uv_size,
464            Vec2 {
465                x: inner.size.x as f32,
466                y: inner.size.y as f32,
467            },
468        );
469
470        let verts = vertex::from_pixels_ex(position, size, tint.as_raw(), degrees, uv_pos, uv_size);
471
472        self.push_rectangle(wgpu, verts);
473    }
474
475    pub fn add_instance_custom(
478        &mut self,
479        points: [Vec2<f32>; 4],
480        uv_points: [Vec2<f32>; 4],
481        degrees: f32,
482        tint: Colour,
483        render: &Renderer,
484    ) {
485        let inner = self.inner.as_ref().unwrap();
486
487        let wgpu = render.wgpu;
488
489        let uv_points = [
490            normalize_points(
491                uv_points[0],
492                Vec2 {
493                    x: inner.size.x as f32,
494                    y: inner.size.y as f32,
495                },
496            ),
497            normalize_points(
498                uv_points[1],
499                Vec2 {
500                    x: inner.size.x as f32,
501                    y: inner.size.y as f32,
502                },
503            ),
504            normalize_points(
505                uv_points[2],
506                Vec2 {
507                    x: inner.size.x as f32,
508                    y: inner.size.y as f32,
509                },
510            ),
511            normalize_points(
512                uv_points[3],
513                Vec2 {
514                    x: inner.size.x as f32,
515                    y: inner.size.y as f32,
516                },
517            ),
518        ];
519
520        let verts = vertex::from_pixels_custom(points, uv_points, degrees, tint.as_raw());
521
522        self.push_rectangle(wgpu, verts);
523    }
524
525    fn push_rectangle(&mut self, wgpu: &WgpuClump, verts: [Vertex; 4]) {
526        let num_verts = self.get_vertex_number() as u16;
527        let inner = self.inner.as_mut().unwrap();
528
529        let vertex_size = std::mem::size_of::<Vertex>() as u64;
530        let index_size = 2;
531
532        let max_verts = inner.vertex_buffer.size();
533        if self.vertex_count + (4 * vertex_size) > max_verts {
534            material::grow_buffer(
535                &mut inner.vertex_buffer,
536                wgpu,
537                1,
538                wgpu::BufferUsages::VERTEX,
539            );
540        }
541
542        let indicies = [
543            num_verts,
544            1 + num_verts,
545            2 + num_verts,
546            3 + num_verts,
547            num_verts,
548            2 + num_verts,
549        ];
550
551        let max_indicies = inner.index_buffer.size();
552        if self.index_count + (6 * index_size) > max_indicies {
553            material::grow_buffer(&mut inner.index_buffer, wgpu, 1, wgpu::BufferUsages::INDEX);
554        }
555
556        wgpu.queue.write_buffer(
557            &inner.vertex_buffer,
558            self.vertex_count,
559            bytemuck::cast_slice(&verts),
560        );
561        wgpu.queue.write_buffer(
562            &inner.index_buffer,
563            self.index_count,
564            bytemuck::cast_slice(&indicies),
565        );
566
567        self.vertex_count += 4 * vertex_size;
568        self.index_count += 6 * index_size;
569    }
570
571    fn get_vertex_number(&self) -> u64 {
572        self.vertex_count / std::mem::size_of::<Vertex>() as u64
573    }
574
575    fn get_index_number(&self) -> u64 {
576        self.index_count / 2
577    }
578
579    fn add_inner(
580        &mut self,
581        wgpu: &WgpuClump,
582        text_handle: &mut TextRenderer,
583        resources: &ResourceManager,
584        sampler: &wgpu::Sampler,
585    ) -> bool {
586        if self.inner.is_none() {
587            self.inner = Some(InnerMaterial::new(
588                wgpu,
589                text_handle,
590                resources,
591                sampler,
592                &self.text,
593                self.font_size,
594                self.line_height,
595                self.colour,
596            ));
597            true
598        } else {
599            false
600        }
601    }
602
603    pub fn prepare(&mut self, engine: &mut Engine) {
607        let context = match &mut engine.context {
608            Some(c) => c,
609            None => return, };
611
612        if self.inner.is_none() {
613            self.inner = Some(InnerMaterial::new(
614                &context.wgpu,
615                &mut context.text_renderer,
616                &engine.resource_manager,
617                &context.texture_sampler,
618                &self.text,
619                self.font_size,
620                self.line_height,
621                self.colour,
622            ));
623        }
624
625        let font_info = FontInformation {
626            wgpu: &context.wgpu,
627            text_handle: &mut context.text_renderer,
628            resources: &engine.resource_manager,
629        };
630
631        let inner = self.inner.as_mut().unwrap();
632
633        let texture_view = inner
634            .texture
635            .create_view(&wgpu::TextureViewDescriptor::default());
636        render_text_to_texture(
637            font_info.text_handle,
638            &inner.text_buffer,
639            inner.bounds,
640            &inner.viewport,
641            &texture_view,
642            font_info.wgpu,
643        );
644
645        inner.bind_group = context
646            .wgpu
647            .device
648            .create_bind_group(&wgpu::BindGroupDescriptor {
649                label: Some("Text Widget BindGroup"),
650                layout: &layouts::create_texture_layout(&context.wgpu.device),
651                entries: &[
652                    wgpu::BindGroupEntry {
653                        binding: 0,
654                        resource: wgpu::BindingResource::TextureView(&texture_view),
655                    },
656                    wgpu::BindGroupEntry {
657                        binding: 1,
658                        resource: wgpu::BindingResource::Sampler(&context.texture_sampler),
659                    },
660                ],
661            });
662    }
663
664    pub fn draw<'others>(&'others mut self, information: &mut Renderer<'_, 'others>) {
666        let inner = self.inner.as_ref().unwrap();
667
668        let pipeline = &information
669            .resources
670            .get_pipeline(&information.defualt_id)
671            .unwrap()
672            .pipeline;
673
674        information.pass.set_pipeline(pipeline);
675        information.pass.set_bind_group(0, &inner.bind_group, &[]);
676
677        information
678            .pass
679            .set_vertex_buffer(0, inner.vertex_buffer.slice(0..self.vertex_count));
680        information.pass.set_index_buffer(
681            inner.index_buffer.slice(0..self.index_count),
682            wgpu::IndexFormat::Uint16,
683        );
684
685        information
686            .pass
687            .draw_indexed(0..self.get_index_number() as u32, 0, 0..1);
688
689        self.vertex_count = 0;
690        self.index_count = 0;
691    }
692}
693
694struct InnerMaterial {
695    size: Vec2<u32>,
696    text_buffer: glyphon::Buffer,
697    vertex_buffer: wgpu::Buffer,
698    index_buffer: wgpu::Buffer,
699    texture: wgpu::Texture,
700    viewport: glyphon::Viewport,
701    bind_group: wgpu::BindGroup,
702    bounds: Vec2<Vec2<i32>>,
703}
704
705impl InnerMaterial {
706    #[allow(clippy::too_many_arguments)]
707    fn new(
708        wgpu: &WgpuClump,
709        text_handle: &mut TextRenderer,
710        resources: &ResourceManager,
711        sampler: &wgpu::Sampler,
712        text: &str,
713        font_size: f32,
714        line_height: f32,
715        colour: Colour,
716    ) -> Self {
717        let font_info = FontInformation {
718            wgpu,
719            text_handle,
720            resources,
721        };
722
723        let mut text_buffer = glyphon::Buffer::new(
724            &mut font_info.text_handle.font_system,
725            Metrics::new(font_size, line_height),
726        );
727
728        text_buffer.set_size(&mut font_info.text_handle.font_system, None, None);
729
730        text_buffer.set_text(
731            &mut font_info.text_handle.font_system,
732            text,
733            Attrs::new()
734                .color(colour.into())
735                .family(Family::Name(&font_info.text_handle.defualt_font_name)),
736            Shaping::Advanced,
737        );
738
739        let hieght = text_buffer.lines.len() as f32 * text_buffer.metrics().line_height;
740        let run_width = text_buffer
741            .layout_runs()
742            .map(|run| run.line_w)
743            .max_by(f32::total_cmp)
744            .unwrap_or(0.0);
745
746        let texture = font_info
747            .wgpu
748            .device
749            .create_texture(&wgpu::TextureDescriptor {
750                label: Some("Text Texture"),
751                size: wgpu::Extent3d {
752                    width: run_width.ceil() as u32,
753                    height: hieght.ceil() as u32,
754                    depth_or_array_layers: 1,
755                },
756                mip_level_count: 1,
757                sample_count: 1,
758                dimension: wgpu::TextureDimension::D2,
759                format: wgpu::TextureFormat::Rgba8UnormSrgb,
760                usage: wgpu::TextureUsages::RENDER_ATTACHMENT
761                    | wgpu::TextureUsages::TEXTURE_BINDING,
762                view_formats: &[],
763            });
764
765        let bounds = Vec2 {
766            x: Vec2 { x: 0, y: 0 },
767            y: Vec2 {
768                x: i32::MAX,
769                y: i32::MAX,
770            },
771        };
772
773        let texture_size = Vec2 {
774            x: run_width.ceil() as u32,
775            y: hieght.ceil() as u32,
776        };
777
778        let mut viewport =
779            glyphon::Viewport::new(&font_info.wgpu.device, &font_info.text_handle.text_cache);
780        viewport.update(&font_info.wgpu.queue, texture_size.into());
781
782        let texture_view = texture.create_view(&Default::default());
783
784        render_text_to_texture(
785            font_info.text_handle,
786            &text_buffer,
787            bounds,
788            &viewport,
789            &texture_view,
790            font_info.wgpu,
791        );
792
793        let vertex_size = std::mem::size_of::<Vertex>() as u64;
794
795        let (vertex_buffer, index_buffer) =
796            Material::<()>::create_buffers(&wgpu.device, vertex_size, 16, 2, 32);
797
798        let bind_group = wgpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
799            label: Some("Text Widget BindGroup"),
800            layout: &layouts::create_texture_layout(&wgpu.device),
801            entries: &[
802                wgpu::BindGroupEntry {
803                    binding: 0,
804                    resource: wgpu::BindingResource::TextureView(&texture_view),
805                },
806                wgpu::BindGroupEntry {
807                    binding: 1,
808                    resource: wgpu::BindingResource::Sampler(sampler),
809                },
810            ],
811        });
812
813        Self {
814            size: texture_size,
815            text_buffer,
816            texture,
817            vertex_buffer,
818            index_buffer,
819            bind_group,
820            viewport,
821            bounds,
822        }
823    }
824}
825
826pub(crate) struct FontInformation<'a> {
828    text_handle: &'a mut TextRenderer,
829    wgpu: &'a WgpuClump,
830    resources: &'a ResourceManager,
831}
832
833pub struct Font {
835    name: String,
836}
837
838impl Font {
839    pub fn new<P: AsRef<Path>>(
841        path: P,
842        engine: &mut Engine,
843        loading_op: LoadingOp,
844    ) -> ResourceId<Font> {
845        let typed_id = resource::generate_id::<Font>();
846        let id = typed_id.get_id();
847        let path = path.as_ref();
848        let ip_resource = InProgressResource::new(path, id, ResourceType::Font, loading_op);
849
850        engine.loader.blocking_load(ip_resource, engine.get_proxy());
851
852        typed_id
853    }
854
855    pub(crate) fn from_str(name: &str) -> Self {
856        Self {
857            name: name.to_string(),
858        }
859    }
860}
861
862fn render_text_to_texture(
863    text_handle: &mut TextRenderer,
864    text: &glyphon::Buffer,
865    bounds: Vec2<Vec2<i32>>,
866    viewport: &glyphon::Viewport,
867    texture_view: &wgpu::TextureView,
868    wgpu: &WgpuClump,
869) {
870    let text_area = TextArea {
871        buffer: text,
872        left: 0.0,
873        top: 0.0,
874        scale: 1.0,
875        bounds: TextBounds {
876            left: bounds.x.x,
877            top: bounds.x.y,
878            right: bounds.y.x,
879            bottom: bounds.y.y,
880        },
881        default_color: glyphon::Color::rgb(255, 255, 255),
882        custom_glyphs: &[],
883    };
884
885    text_handle
886        .text_renderer
887        .prepare(
888            &wgpu.device,
889            &wgpu.queue,
890            &mut text_handle.font_system,
891            &mut text_handle.atlas,
892            viewport,
893            [text_area],
894            &mut text_handle.swash_cache,
895        )
896        .unwrap_or(());
897
898    let mut text_encoder = wgpu
899        .device
900        .create_command_encoder(&wgpu::CommandEncoderDescriptor {
901            label: Some("text encoder"),
902        });
903
904    let mut text_pass = text_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
905        label: Some("Text to Texture Pass"),
906        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
907            view: texture_view,
908            resolve_target: None,
909            ops: wgpu::Operations {
910                load: wgpu::LoadOp::Clear(wgpu::Color {
911                    r: 0.0,
912                    g: 0.0,
913                    b: 0.0,
914                    a: 0.0,
915                }),
916                store: wgpu::StoreOp::Store,
917            },
918        })],
919        timestamp_writes: None,
920        occlusion_query_set: None,
921        depth_stencil_attachment: None,
922    });
923
924    text_handle
925        .text_renderer
926        .render(&text_handle.atlas, viewport, &mut text_pass)
927        .unwrap();
928
929    drop(text_pass);
930    text_handle.atlas.trim();
931
932    wgpu.queue.submit(std::iter::once(text_encoder.finish()));
933}