1use crate::error::Result;
2use crate::page::Margins;
3use crate::text::{measure_text, split_into_words, Font};
4use std::fmt::Write;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub enum TextAlign {
8 Left,
9 Right,
10 Center,
11 Justified,
12}
13
14pub struct TextFlowContext {
15 operations: String,
16 current_font: Font,
17 font_size: f64,
18 line_height: f64,
19 cursor_x: f64,
20 cursor_y: f64,
21 alignment: TextAlign,
22 page_width: f64,
23 #[allow(dead_code)]
24 page_height: f64,
25 margins: Margins,
26}
27
28impl TextFlowContext {
29 pub fn new(page_width: f64, page_height: f64, margins: Margins) -> Self {
30 Self {
31 operations: String::new(),
32 current_font: Font::Helvetica,
33 font_size: 12.0,
34 line_height: 1.2,
35 cursor_x: margins.left,
36 cursor_y: page_height - margins.top,
37 alignment: TextAlign::Left,
38 page_width,
39 page_height,
40 margins,
41 }
42 }
43
44 pub fn set_font(&mut self, font: Font, size: f64) -> &mut Self {
45 self.current_font = font;
46 self.font_size = size;
47 self
48 }
49
50 pub fn set_line_height(&mut self, multiplier: f64) -> &mut Self {
51 self.line_height = multiplier;
52 self
53 }
54
55 pub fn set_alignment(&mut self, alignment: TextAlign) -> &mut Self {
56 self.alignment = alignment;
57 self
58 }
59
60 pub fn at(&mut self, x: f64, y: f64) -> &mut Self {
61 self.cursor_x = x;
62 self.cursor_y = y;
63 self
64 }
65
66 pub fn content_width(&self) -> f64 {
67 self.page_width - self.margins.left - self.margins.right
68 }
69
70 pub fn write_wrapped(&mut self, text: &str) -> Result<&mut Self> {
71 let content_width = self.content_width();
72
73 let words = split_into_words(text);
75 let mut lines: Vec<Vec<&str>> = Vec::new();
76 let mut current_line: Vec<&str> = Vec::new();
77 let mut current_width = 0.0;
78
79 for word in words {
81 let word_width = measure_text(word, self.current_font.clone(), self.font_size);
82
83 if !current_line.is_empty() && current_width + word_width > content_width {
85 lines.push(current_line);
86 current_line = vec![word];
87 current_width = word_width;
88 } else {
89 current_line.push(word);
90 current_width += word_width;
91 }
92 }
93
94 if !current_line.is_empty() {
95 lines.push(current_line);
96 }
97
98 for (i, line) in lines.iter().enumerate() {
100 let line_text = line.join("");
101 let line_width = measure_text(&line_text, self.current_font.clone(), self.font_size);
102
103 let x = match self.alignment {
105 TextAlign::Left => self.margins.left,
106 TextAlign::Right => self.page_width - self.margins.right - line_width,
107 TextAlign::Center => self.margins.left + (content_width - line_width) / 2.0,
108 TextAlign::Justified => {
109 if i < lines.len() - 1 && line.len() > 1 {
110 self.margins.left
112 } else {
113 self.margins.left
114 }
115 }
116 };
117
118 self.operations.push_str("BT\n");
120
121 writeln!(
123 &mut self.operations,
124 "/{} {} Tf",
125 self.current_font.pdf_name(),
126 self.font_size
127 )
128 .unwrap();
129
130 writeln!(&mut self.operations, "{:.2} {:.2} Td", x, self.cursor_y).unwrap();
132
133 if self.alignment == TextAlign::Justified && i < lines.len() - 1 && line.len() > 1 {
135 let spaces_count = line.iter().filter(|w| w.trim().is_empty()).count();
137 if spaces_count > 0 {
138 let extra_space = content_width - line_width;
139 let space_adjustment = extra_space / spaces_count as f64;
140
141 writeln!(&mut self.operations, "{space_adjustment:.2} Tw").unwrap();
143 }
144 }
145
146 self.operations.push('(');
148 for ch in line_text.chars() {
149 match ch {
150 '(' => self.operations.push_str("\\("),
151 ')' => self.operations.push_str("\\)"),
152 '\\' => self.operations.push_str("\\\\"),
153 '\n' => self.operations.push_str("\\n"),
154 '\r' => self.operations.push_str("\\r"),
155 '\t' => self.operations.push_str("\\t"),
156 _ => self.operations.push(ch),
157 }
158 }
159 self.operations.push_str(") Tj\n");
160
161 if self.alignment == TextAlign::Justified && i < lines.len() - 1 {
163 self.operations.push_str("0 Tw\n");
164 }
165
166 self.operations.push_str("ET\n");
168
169 self.cursor_y -= self.font_size * self.line_height;
171 }
172
173 Ok(self)
174 }
175
176 pub fn write_paragraph(&mut self, text: &str) -> Result<&mut Self> {
177 self.write_wrapped(text)?;
178 self.cursor_y -= self.font_size * self.line_height * 0.5;
180 Ok(self)
181 }
182
183 pub fn newline(&mut self) -> &mut Self {
184 self.cursor_y -= self.font_size * self.line_height;
185 self.cursor_x = self.margins.left;
186 self
187 }
188
189 pub fn cursor_position(&self) -> (f64, f64) {
190 (self.cursor_x, self.cursor_y)
191 }
192
193 pub fn generate_operations(&self) -> Vec<u8> {
194 self.operations.as_bytes().to_vec()
195 }
196
197 pub fn alignment(&self) -> TextAlign {
199 self.alignment
200 }
201
202 pub fn page_dimensions(&self) -> (f64, f64) {
204 (self.page_width, self.page_height)
205 }
206
207 pub fn margins(&self) -> &Margins {
209 &self.margins
210 }
211
212 pub fn line_height(&self) -> f64 {
214 self.line_height
215 }
216
217 pub fn operations(&self) -> &str {
219 &self.operations
220 }
221
222 pub fn clear(&mut self) {
224 self.operations.clear();
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use crate::page::Margins;
232
233 fn create_test_margins() -> Margins {
234 Margins {
235 left: 50.0,
236 right: 50.0,
237 top: 50.0,
238 bottom: 50.0,
239 }
240 }
241
242 #[test]
243 fn test_text_flow_context_new() {
244 let margins = create_test_margins();
245 let context = TextFlowContext::new(400.0, 600.0, margins.clone());
246
247 assert_eq!(context.current_font, Font::Helvetica);
248 assert_eq!(context.font_size, 12.0);
249 assert_eq!(context.line_height, 1.2);
250 assert_eq!(context.alignment, TextAlign::Left);
251 assert_eq!(context.page_width, 400.0);
252 assert_eq!(context.page_height, 600.0);
253 assert_eq!(context.cursor_x, 50.0); assert_eq!(context.cursor_y, 550.0); }
256
257 #[test]
258 fn test_set_font() {
259 let margins = create_test_margins();
260 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
261
262 context.set_font(Font::TimesBold, 16.0);
263 assert_eq!(context.current_font, Font::TimesBold);
264 assert_eq!(context.font_size, 16.0);
265 }
266
267 #[test]
268 fn test_set_line_height() {
269 let margins = create_test_margins();
270 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
271
272 context.set_line_height(1.5);
273 assert_eq!(context.line_height(), 1.5);
274 }
275
276 #[test]
277 fn test_set_alignment() {
278 let margins = create_test_margins();
279 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
280
281 context.set_alignment(TextAlign::Center);
282 assert_eq!(context.alignment(), TextAlign::Center);
283 }
284
285 #[test]
286 fn test_at_position() {
287 let margins = create_test_margins();
288 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
289
290 context.at(100.0, 200.0);
291 let (x, y) = context.cursor_position();
292 assert_eq!(x, 100.0);
293 assert_eq!(y, 200.0);
294 }
295
296 #[test]
297 fn test_content_width() {
298 let margins = create_test_margins();
299 let context = TextFlowContext::new(400.0, 600.0, margins.clone());
300
301 let content_width = context.content_width();
302 assert_eq!(content_width, 300.0); }
304
305 #[test]
306 fn test_text_align_variants() {
307 assert_eq!(TextAlign::Left, TextAlign::Left);
308 assert_eq!(TextAlign::Right, TextAlign::Right);
309 assert_eq!(TextAlign::Center, TextAlign::Center);
310 assert_eq!(TextAlign::Justified, TextAlign::Justified);
311
312 assert_ne!(TextAlign::Left, TextAlign::Right);
313 }
314
315 #[test]
316 fn test_write_wrapped_simple() {
317 let margins = create_test_margins();
318 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
319
320 context.write_wrapped("Hello World").unwrap();
321
322 let ops = context.operations();
323 assert!(ops.contains("BT\n"));
324 assert!(ops.contains("ET\n"));
325 assert!(ops.contains("/Helvetica 12 Tf"));
326 assert!(ops.contains("(Hello World) Tj"));
327 }
328
329 #[test]
330 fn test_write_paragraph() {
331 let margins = create_test_margins();
332 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
333
334 let initial_y = context.cursor_y;
335 context.write_paragraph("Test paragraph").unwrap();
336
337 assert!(context.cursor_y < initial_y);
339 }
340
341 #[test]
342 fn test_newline() {
343 let margins = create_test_margins();
344 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
345
346 let initial_y = context.cursor_y;
347 context.newline();
348
349 assert_eq!(context.cursor_x, margins.left);
350 assert!(context.cursor_y < initial_y);
351 assert_eq!(
352 context.cursor_y,
353 initial_y - context.font_size * context.line_height
354 );
355 }
356
357 #[test]
358 fn test_cursor_position() {
359 let margins = create_test_margins();
360 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
361
362 context.at(75.0, 125.0);
363 let (x, y) = context.cursor_position();
364 assert_eq!(x, 75.0);
365 assert_eq!(y, 125.0);
366 }
367
368 #[test]
369 fn test_generate_operations() {
370 let margins = create_test_margins();
371 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
372
373 context.write_wrapped("Test").unwrap();
374 let ops_bytes = context.generate_operations();
375 let ops_string = String::from_utf8(ops_bytes).unwrap();
376
377 assert_eq!(ops_string, context.operations());
378 }
379
380 #[test]
381 fn test_clear_operations() {
382 let margins = create_test_margins();
383 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
384
385 context.write_wrapped("Test").unwrap();
386 assert!(!context.operations().is_empty());
387
388 context.clear();
389 assert!(context.operations().is_empty());
390 }
391
392 #[test]
393 fn test_page_dimensions() {
394 let margins = create_test_margins();
395 let context = TextFlowContext::new(400.0, 600.0, margins.clone());
396
397 let (width, height) = context.page_dimensions();
398 assert_eq!(width, 400.0);
399 assert_eq!(height, 600.0);
400 }
401
402 #[test]
403 fn test_margins_access() {
404 let margins = create_test_margins();
405 let context = TextFlowContext::new(400.0, 600.0, margins.clone());
406
407 let ctx_margins = context.margins();
408 assert_eq!(ctx_margins.left, 50.0);
409 assert_eq!(ctx_margins.right, 50.0);
410 assert_eq!(ctx_margins.top, 50.0);
411 assert_eq!(ctx_margins.bottom, 50.0);
412 }
413
414 #[test]
415 fn test_method_chaining() {
416 let margins = create_test_margins();
417 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
418
419 context
420 .set_font(Font::Courier, 10.0)
421 .set_line_height(1.5)
422 .set_alignment(TextAlign::Center)
423 .at(100.0, 200.0);
424
425 assert_eq!(context.current_font, Font::Courier);
426 assert_eq!(context.font_size, 10.0);
427 assert_eq!(context.line_height(), 1.5);
428 assert_eq!(context.alignment(), TextAlign::Center);
429 let (x, y) = context.cursor_position();
430 assert_eq!(x, 100.0);
431 assert_eq!(y, 200.0);
432 }
433
434 #[test]
435 fn test_text_align_debug() {
436 let align = TextAlign::Center;
437 let debug_str = format!("{:?}", align);
438 assert_eq!(debug_str, "Center");
439 }
440
441 #[test]
442 fn test_text_align_clone() {
443 let align1 = TextAlign::Justified;
444 let align2 = align1;
445 assert_eq!(align1, align2);
446 }
447
448 #[test]
449 fn test_text_align_copy() {
450 let align1 = TextAlign::Right;
451 let align2 = align1; assert_eq!(align1, align2);
453
454 assert_eq!(align1, TextAlign::Right);
456 assert_eq!(align2, TextAlign::Right);
457 }
458
459 #[test]
460 fn test_write_wrapped_with_alignment_right() {
461 let margins = create_test_margins();
462 let mut context = TextFlowContext::new(400.0, 600.0, margins);
463
464 context.set_alignment(TextAlign::Right);
465 context.write_wrapped("Right aligned text").unwrap();
466
467 let ops = context.operations();
468 assert!(ops.contains("BT\n"));
469 assert!(ops.contains("ET\n"));
470 assert!(ops.contains("Td"));
472 }
473
474 #[test]
475 fn test_write_wrapped_with_alignment_center() {
476 let margins = create_test_margins();
477 let mut context = TextFlowContext::new(400.0, 600.0, margins);
478
479 context.set_alignment(TextAlign::Center);
480 context.write_wrapped("Centered text").unwrap();
481
482 let ops = context.operations();
483 assert!(ops.contains("BT\n"));
484 assert!(ops.contains("(Centered text) Tj"));
485 }
486
487 #[test]
488 fn test_write_wrapped_with_alignment_justified() {
489 let margins = create_test_margins();
490 let mut context = TextFlowContext::new(400.0, 600.0, margins);
491
492 context.set_alignment(TextAlign::Justified);
493 context.write_wrapped("This is a longer text that should wrap across multiple lines to test justification").unwrap();
495
496 let ops = context.operations();
497 assert!(ops.contains("BT\n"));
498 assert!(ops.contains("Tw") || ops.contains("0 Tw"));
500 }
501
502 #[test]
503 fn test_write_wrapped_empty_text() {
504 let margins = create_test_margins();
505 let mut context = TextFlowContext::new(400.0, 600.0, margins);
506
507 context.write_wrapped("").unwrap();
508
509 assert!(context.operations().is_empty());
511 }
512
513 #[test]
514 fn test_write_wrapped_whitespace_only() {
515 let margins = create_test_margins();
516 let mut context = TextFlowContext::new(400.0, 600.0, margins);
517
518 context.write_wrapped(" ").unwrap();
519
520 let ops = context.operations();
521 assert!(ops.contains("BT\n") || ops.is_empty());
523 }
524
525 #[test]
526 fn test_write_wrapped_special_characters() {
527 let margins = create_test_margins();
528 let mut context = TextFlowContext::new(400.0, 600.0, margins);
529
530 context
531 .write_wrapped("Text with (parentheses) and \\backslash\\")
532 .unwrap();
533
534 let ops = context.operations();
535 assert!(ops.contains("\\(parentheses\\)"));
537 assert!(ops.contains("\\\\backslash\\\\"));
538 }
539
540 #[test]
541 fn test_write_wrapped_newlines_tabs() {
542 let margins = create_test_margins();
543 let mut context = TextFlowContext::new(400.0, 600.0, margins);
544
545 context.write_wrapped("Line1\nLine2\tTabbed").unwrap();
546
547 let ops = context.operations();
548 assert!(ops.contains("\\n") || ops.contains("\\t"));
550 }
551
552 #[test]
553 fn test_write_wrapped_very_long_word() {
554 let margins = create_test_margins();
555 let mut context = TextFlowContext::new(200.0, 600.0, margins); let long_word = "a".repeat(100);
558 context.write_wrapped(&long_word).unwrap();
559
560 let ops = context.operations();
561 assert!(ops.contains("BT\n"));
562 assert!(ops.contains(&long_word));
563 }
564
565 #[test]
566 fn test_write_wrapped_cursor_movement() {
567 let margins = create_test_margins();
568 let mut context = TextFlowContext::new(400.0, 600.0, margins);
569
570 let initial_y = context.cursor_y;
571
572 context.write_wrapped("Line 1").unwrap();
573 let y_after_line1 = context.cursor_y;
574
575 context.write_wrapped("Line 2").unwrap();
576 let y_after_line2 = context.cursor_y;
577
578 assert!(y_after_line1 < initial_y);
580 assert!(y_after_line2 < y_after_line1);
581 }
582
583 #[test]
584 fn test_write_paragraph_spacing() {
585 let margins = create_test_margins();
586 let mut context = TextFlowContext::new(400.0, 600.0, margins);
587
588 let initial_y = context.cursor_y;
589 context.write_paragraph("Paragraph 1").unwrap();
590 let y_after_p1 = context.cursor_y;
591
592 context.write_paragraph("Paragraph 2").unwrap();
593 let y_after_p2 = context.cursor_y;
594
595 let spacing1 = initial_y - y_after_p1;
597 let spacing2 = y_after_p1 - y_after_p2;
598
599 assert!(spacing1 > 0.0);
600 assert!(spacing2 > 0.0);
601 }
602
603 #[test]
604 fn test_multiple_newlines() {
605 let margins = create_test_margins();
606 let mut context = TextFlowContext::new(400.0, 600.0, margins);
607
608 let initial_y = context.cursor_y;
609
610 context.newline();
611 let y1 = context.cursor_y;
612
613 context.newline();
614 let y2 = context.cursor_y;
615
616 context.newline();
617 let y3 = context.cursor_y;
618
619 let spacing1 = initial_y - y1;
621 let spacing2 = y1 - y2;
622 let spacing3 = y2 - y3;
623
624 assert!((spacing1 - spacing2).abs() < 1e-10);
626 assert!((spacing2 - spacing3).abs() < 1e-10);
627 assert!((spacing1 - context.font_size * context.line_height).abs() < 1e-10);
628 }
629
630 #[test]
631 fn test_content_width_different_margins() {
632 let margins = Margins {
633 left: 30.0,
634 right: 70.0,
635 top: 40.0,
636 bottom: 60.0,
637 };
638 let context = TextFlowContext::new(500.0, 700.0, margins);
639
640 let content_width = context.content_width();
641 assert_eq!(content_width, 400.0); }
643
644 #[test]
645 fn test_custom_line_height() {
646 let margins = create_test_margins();
647 let mut context = TextFlowContext::new(400.0, 600.0, margins);
648
649 context.set_line_height(2.0);
650
651 let initial_y = context.cursor_y;
652 context.newline();
653 let y_after = context.cursor_y;
654
655 let spacing = initial_y - y_after;
656 assert_eq!(spacing, context.font_size * 2.0); }
658
659 #[test]
660 fn test_different_fonts() {
661 let margins = create_test_margins();
662 let mut context = TextFlowContext::new(400.0, 600.0, margins);
663
664 let fonts = vec![
665 Font::Helvetica,
666 Font::HelveticaBold,
667 Font::TimesRoman,
668 Font::TimesBold,
669 Font::Courier,
670 Font::CourierBold,
671 ];
672
673 for font in fonts {
674 context.clear();
675 let font_name = font.pdf_name();
676 context.set_font(font, 14.0);
677 context.write_wrapped("Test text").unwrap();
678
679 let ops = context.operations();
680 assert!(ops.contains(&format!("/{} 14 Tf", font_name)));
681 }
682 }
683
684 #[test]
685 fn test_font_size_variations() {
686 let margins = create_test_margins();
687 let mut context = TextFlowContext::new(400.0, 600.0, margins);
688
689 let sizes = vec![8.0, 10.0, 12.0, 14.0, 16.0, 24.0, 36.0];
690
691 for size in sizes {
692 context.clear();
693 context.set_font(Font::Helvetica, size);
694 context.write_wrapped("Test").unwrap();
695
696 let ops = context.operations();
697 assert!(ops.contains(&format!("/Helvetica {} Tf", size)));
698 }
699 }
700
701 #[test]
702 fn test_at_position_edge_cases() {
703 let margins = create_test_margins();
704 let mut context = TextFlowContext::new(400.0, 600.0, margins);
705
706 context.at(0.0, 0.0);
708 assert_eq!(context.cursor_position(), (0.0, 0.0));
709
710 context.at(-10.0, -20.0);
712 assert_eq!(context.cursor_position(), (-10.0, -20.0));
713
714 context.at(10000.0, 20000.0);
716 assert_eq!(context.cursor_position(), (10000.0, 20000.0));
717 }
718
719 #[test]
720 fn test_write_wrapped_with_narrow_content() {
721 let margins = Margins {
722 left: 190.0,
723 right: 190.0,
724 top: 50.0,
725 bottom: 50.0,
726 };
727 let mut context = TextFlowContext::new(400.0, 600.0, margins);
728
729 context
731 .write_wrapped("This text should wrap a lot")
732 .unwrap();
733
734 let ops = context.operations();
735 let bt_count = ops.matches("BT\n").count();
737 assert!(bt_count > 1);
738 }
739
740 #[test]
741 fn test_justified_text_single_word_line() {
742 let margins = create_test_margins();
743 let mut context = TextFlowContext::new(400.0, 600.0, margins);
744
745 context.set_alignment(TextAlign::Justified);
746 context.write_wrapped("SingleWord").unwrap();
747
748 let ops = context.operations();
749 assert!(!ops.contains(" Tw") || ops.contains("0 Tw"));
751 }
752
753 #[test]
754 fn test_justified_text_last_line() {
755 let margins = create_test_margins();
756 let mut context = TextFlowContext::new(400.0, 600.0, margins);
757
758 context.set_alignment(TextAlign::Justified);
759 context.write_wrapped("This is a test of justified text alignment where the last line should not be justified").unwrap();
761
762 let ops = context.operations();
763 assert!(ops.contains("0 Tw"));
765 }
766
767 #[test]
768 fn test_generate_operations_encoding() {
769 let margins = create_test_margins();
770 let mut context = TextFlowContext::new(400.0, 600.0, margins);
771
772 context.write_wrapped("UTF-8 Text: Ñ").unwrap();
773
774 let ops_bytes = context.generate_operations();
775 let ops_string = String::from_utf8(ops_bytes.clone()).unwrap();
776
777 assert_eq!(ops_bytes, context.operations().as_bytes());
778 assert_eq!(ops_string, context.operations());
779 }
780
781 #[test]
782 fn test_clear_resets_operations_only() {
783 let margins = create_test_margins();
784 let mut context = TextFlowContext::new(400.0, 600.0, margins);
785
786 context.set_font(Font::TimesBold, 18.0);
787 context.set_alignment(TextAlign::Right);
788 context.at(100.0, 200.0);
789 context.write_wrapped("Text").unwrap();
790
791 context.clear();
792
793 assert!(context.operations().is_empty());
795
796 assert_eq!(context.current_font, Font::TimesBold);
798 assert_eq!(context.font_size, 18.0);
799 assert_eq!(context.alignment(), TextAlign::Right);
800 let (x, y) = context.cursor_position();
802 assert_eq!(x, 100.0); assert!(y < 200.0); }
805
806 #[test]
807 fn test_long_text_wrapping() {
808 let margins = create_test_margins();
809 let mut context = TextFlowContext::new(400.0, 600.0, margins);
810
811 let long_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
812 Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
813 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.";
814
815 context.write_wrapped(long_text).unwrap();
816
817 let ops = context.operations();
818 let tj_count = ops.matches(") Tj").count();
820 assert!(tj_count > 1);
821 }
822
823 #[test]
824 fn test_empty_operations_initially() {
825 let margins = create_test_margins();
826 let context = TextFlowContext::new(400.0, 600.0, margins);
827
828 assert!(context.operations().is_empty());
829 assert_eq!(context.generate_operations().len(), 0);
830 }
831
832 #[test]
833 fn test_write_paragraph_empty() {
834 let margins = create_test_margins();
835 let mut context = TextFlowContext::new(400.0, 600.0, margins);
836
837 let initial_y = context.cursor_y;
838 context.write_paragraph("").unwrap();
839
840 assert!(context.cursor_y < initial_y);
842 }
843
844 #[test]
845 fn test_extreme_line_height() {
846 let margins = create_test_margins();
847 let mut context = TextFlowContext::new(400.0, 600.0, margins);
848
849 context.set_line_height(0.1);
851 let initial_y = context.cursor_y;
852 context.newline();
853 assert_eq!(context.cursor_y, initial_y - context.font_size * 0.1);
854
855 context.set_line_height(10.0);
857 let initial_y2 = context.cursor_y;
858 context.newline();
859 assert_eq!(context.cursor_y, initial_y2 - context.font_size * 10.0);
860 }
861
862 #[test]
863 fn test_zero_content_width() {
864 let margins = Margins {
865 left: 200.0,
866 right: 200.0,
867 top: 50.0,
868 bottom: 50.0,
869 };
870 let context = TextFlowContext::new(400.0, 600.0, margins);
871
872 assert_eq!(context.content_width(), 0.0);
873 }
874
875 #[test]
876 fn test_cursor_x_reset_on_newline() {
877 let margins = create_test_margins();
878 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
879
880 context.at(250.0, 300.0); context.newline();
882
883 assert_eq!(context.cursor_x, margins.left);
885 assert_eq!(
887 context.cursor_y,
888 300.0 - context.font_size * context.line_height
889 );
890 }
891}