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 .expect("Writing to String should never fail");
129
130 writeln!(&mut self.operations, "{:.2} {:.2} Td", x, self.cursor_y)
132 .expect("Writing to String should never fail");
133
134 if self.alignment == TextAlign::Justified && i < lines.len() - 1 && line.len() > 1 {
136 let spaces_count = line.iter().filter(|w| w.trim().is_empty()).count();
138 if spaces_count > 0 {
139 let extra_space = content_width - line_width;
140 let space_adjustment = extra_space / spaces_count as f64;
141
142 writeln!(&mut self.operations, "{space_adjustment:.2} Tw")
144 .expect("Writing to String should never fail");
145 }
146 }
147
148 self.operations.push('(');
150 for ch in line_text.chars() {
151 match ch {
152 '(' => self.operations.push_str("\\("),
153 ')' => self.operations.push_str("\\)"),
154 '\\' => self.operations.push_str("\\\\"),
155 '\n' => self.operations.push_str("\\n"),
156 '\r' => self.operations.push_str("\\r"),
157 '\t' => self.operations.push_str("\\t"),
158 _ => self.operations.push(ch),
159 }
160 }
161 self.operations.push_str(") Tj\n");
162
163 if self.alignment == TextAlign::Justified && i < lines.len() - 1 {
165 self.operations.push_str("0 Tw\n");
166 }
167
168 self.operations.push_str("ET\n");
170
171 self.cursor_y -= self.font_size * self.line_height;
173 }
174
175 Ok(self)
176 }
177
178 pub fn write_paragraph(&mut self, text: &str) -> Result<&mut Self> {
179 self.write_wrapped(text)?;
180 self.cursor_y -= self.font_size * self.line_height * 0.5;
182 Ok(self)
183 }
184
185 pub fn newline(&mut self) -> &mut Self {
186 self.cursor_y -= self.font_size * self.line_height;
187 self.cursor_x = self.margins.left;
188 self
189 }
190
191 pub fn cursor_position(&self) -> (f64, f64) {
192 (self.cursor_x, self.cursor_y)
193 }
194
195 pub fn generate_operations(&self) -> Vec<u8> {
196 self.operations.as_bytes().to_vec()
197 }
198
199 pub fn alignment(&self) -> TextAlign {
201 self.alignment
202 }
203
204 pub fn page_dimensions(&self) -> (f64, f64) {
206 (self.page_width, self.page_height)
207 }
208
209 pub fn margins(&self) -> &Margins {
211 &self.margins
212 }
213
214 pub fn line_height(&self) -> f64 {
216 self.line_height
217 }
218
219 pub fn operations(&self) -> &str {
221 &self.operations
222 }
223
224 pub fn clear(&mut self) {
226 self.operations.clear();
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use crate::page::Margins;
234
235 fn create_test_margins() -> Margins {
236 Margins {
237 left: 50.0,
238 right: 50.0,
239 top: 50.0,
240 bottom: 50.0,
241 }
242 }
243
244 #[test]
245 fn test_text_flow_context_new() {
246 let margins = create_test_margins();
247 let context = TextFlowContext::new(400.0, 600.0, margins.clone());
248
249 assert_eq!(context.current_font, Font::Helvetica);
250 assert_eq!(context.font_size, 12.0);
251 assert_eq!(context.line_height, 1.2);
252 assert_eq!(context.alignment, TextAlign::Left);
253 assert_eq!(context.page_width, 400.0);
254 assert_eq!(context.page_height, 600.0);
255 assert_eq!(context.cursor_x, 50.0); assert_eq!(context.cursor_y, 550.0); }
258
259 #[test]
260 fn test_set_font() {
261 let margins = create_test_margins();
262 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
263
264 context.set_font(Font::TimesBold, 16.0);
265 assert_eq!(context.current_font, Font::TimesBold);
266 assert_eq!(context.font_size, 16.0);
267 }
268
269 #[test]
270 fn test_set_line_height() {
271 let margins = create_test_margins();
272 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
273
274 context.set_line_height(1.5);
275 assert_eq!(context.line_height(), 1.5);
276 }
277
278 #[test]
279 fn test_set_alignment() {
280 let margins = create_test_margins();
281 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
282
283 context.set_alignment(TextAlign::Center);
284 assert_eq!(context.alignment(), TextAlign::Center);
285 }
286
287 #[test]
288 fn test_at_position() {
289 let margins = create_test_margins();
290 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
291
292 context.at(100.0, 200.0);
293 let (x, y) = context.cursor_position();
294 assert_eq!(x, 100.0);
295 assert_eq!(y, 200.0);
296 }
297
298 #[test]
299 fn test_content_width() {
300 let margins = create_test_margins();
301 let context = TextFlowContext::new(400.0, 600.0, margins.clone());
302
303 let content_width = context.content_width();
304 assert_eq!(content_width, 300.0); }
306
307 #[test]
308 fn test_text_align_variants() {
309 assert_eq!(TextAlign::Left, TextAlign::Left);
310 assert_eq!(TextAlign::Right, TextAlign::Right);
311 assert_eq!(TextAlign::Center, TextAlign::Center);
312 assert_eq!(TextAlign::Justified, TextAlign::Justified);
313
314 assert_ne!(TextAlign::Left, TextAlign::Right);
315 }
316
317 #[test]
318 fn test_write_wrapped_simple() {
319 let margins = create_test_margins();
320 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
321
322 context.write_wrapped("Hello World").unwrap();
323
324 let ops = context.operations();
325 assert!(ops.contains("BT\n"));
326 assert!(ops.contains("ET\n"));
327 assert!(ops.contains("/Helvetica 12 Tf"));
328 assert!(ops.contains("(Hello World) Tj"));
329 }
330
331 #[test]
332 fn test_write_paragraph() {
333 let margins = create_test_margins();
334 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
335
336 let initial_y = context.cursor_y;
337 context.write_paragraph("Test paragraph").unwrap();
338
339 assert!(context.cursor_y < initial_y);
341 }
342
343 #[test]
344 fn test_newline() {
345 let margins = create_test_margins();
346 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
347
348 let initial_y = context.cursor_y;
349 context.newline();
350
351 assert_eq!(context.cursor_x, margins.left);
352 assert!(context.cursor_y < initial_y);
353 assert_eq!(
354 context.cursor_y,
355 initial_y - context.font_size * context.line_height
356 );
357 }
358
359 #[test]
360 fn test_cursor_position() {
361 let margins = create_test_margins();
362 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
363
364 context.at(75.0, 125.0);
365 let (x, y) = context.cursor_position();
366 assert_eq!(x, 75.0);
367 assert_eq!(y, 125.0);
368 }
369
370 #[test]
371 fn test_generate_operations() {
372 let margins = create_test_margins();
373 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
374
375 context.write_wrapped("Test").unwrap();
376 let ops_bytes = context.generate_operations();
377 let ops_string = String::from_utf8(ops_bytes).unwrap();
378
379 assert_eq!(ops_string, context.operations());
380 }
381
382 #[test]
383 fn test_clear_operations() {
384 let margins = create_test_margins();
385 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
386
387 context.write_wrapped("Test").unwrap();
388 assert!(!context.operations().is_empty());
389
390 context.clear();
391 assert!(context.operations().is_empty());
392 }
393
394 #[test]
395 fn test_page_dimensions() {
396 let margins = create_test_margins();
397 let context = TextFlowContext::new(400.0, 600.0, margins.clone());
398
399 let (width, height) = context.page_dimensions();
400 assert_eq!(width, 400.0);
401 assert_eq!(height, 600.0);
402 }
403
404 #[test]
405 fn test_margins_access() {
406 let margins = create_test_margins();
407 let context = TextFlowContext::new(400.0, 600.0, margins.clone());
408
409 let ctx_margins = context.margins();
410 assert_eq!(ctx_margins.left, 50.0);
411 assert_eq!(ctx_margins.right, 50.0);
412 assert_eq!(ctx_margins.top, 50.0);
413 assert_eq!(ctx_margins.bottom, 50.0);
414 }
415
416 #[test]
417 fn test_method_chaining() {
418 let margins = create_test_margins();
419 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
420
421 context
422 .set_font(Font::Courier, 10.0)
423 .set_line_height(1.5)
424 .set_alignment(TextAlign::Center)
425 .at(100.0, 200.0);
426
427 assert_eq!(context.current_font, Font::Courier);
428 assert_eq!(context.font_size, 10.0);
429 assert_eq!(context.line_height(), 1.5);
430 assert_eq!(context.alignment(), TextAlign::Center);
431 let (x, y) = context.cursor_position();
432 assert_eq!(x, 100.0);
433 assert_eq!(y, 200.0);
434 }
435
436 #[test]
437 fn test_text_align_debug() {
438 let align = TextAlign::Center;
439 let debug_str = format!("{align:?}");
440 assert_eq!(debug_str, "Center");
441 }
442
443 #[test]
444 fn test_text_align_clone() {
445 let align1 = TextAlign::Justified;
446 let align2 = align1;
447 assert_eq!(align1, align2);
448 }
449
450 #[test]
451 fn test_text_align_copy() {
452 let align1 = TextAlign::Right;
453 let align2 = align1; assert_eq!(align1, align2);
455
456 assert_eq!(align1, TextAlign::Right);
458 assert_eq!(align2, TextAlign::Right);
459 }
460
461 #[test]
462 fn test_write_wrapped_with_alignment_right() {
463 let margins = create_test_margins();
464 let mut context = TextFlowContext::new(400.0, 600.0, margins);
465
466 context.set_alignment(TextAlign::Right);
467 context.write_wrapped("Right aligned text").unwrap();
468
469 let ops = context.operations();
470 assert!(ops.contains("BT\n"));
471 assert!(ops.contains("ET\n"));
472 assert!(ops.contains("Td"));
474 }
475
476 #[test]
477 fn test_write_wrapped_with_alignment_center() {
478 let margins = create_test_margins();
479 let mut context = TextFlowContext::new(400.0, 600.0, margins);
480
481 context.set_alignment(TextAlign::Center);
482 context.write_wrapped("Centered text").unwrap();
483
484 let ops = context.operations();
485 assert!(ops.contains("BT\n"));
486 assert!(ops.contains("(Centered text) Tj"));
487 }
488
489 #[test]
490 fn test_write_wrapped_with_alignment_justified() {
491 let margins = create_test_margins();
492 let mut context = TextFlowContext::new(400.0, 600.0, margins);
493
494 context.set_alignment(TextAlign::Justified);
495 context.write_wrapped("This is a longer text that should wrap across multiple lines to test justification").unwrap();
497
498 let ops = context.operations();
499 assert!(ops.contains("BT\n"));
500 assert!(ops.contains("Tw") || ops.contains("0 Tw"));
502 }
503
504 #[test]
505 fn test_write_wrapped_empty_text() {
506 let margins = create_test_margins();
507 let mut context = TextFlowContext::new(400.0, 600.0, margins);
508
509 context.write_wrapped("").unwrap();
510
511 assert!(context.operations().is_empty());
513 }
514
515 #[test]
516 fn test_write_wrapped_whitespace_only() {
517 let margins = create_test_margins();
518 let mut context = TextFlowContext::new(400.0, 600.0, margins);
519
520 context.write_wrapped(" ").unwrap();
521
522 let ops = context.operations();
523 assert!(ops.contains("BT\n") || ops.is_empty());
525 }
526
527 #[test]
528 fn test_write_wrapped_special_characters() {
529 let margins = create_test_margins();
530 let mut context = TextFlowContext::new(400.0, 600.0, margins);
531
532 context
533 .write_wrapped("Text with (parentheses) and \\backslash\\")
534 .unwrap();
535
536 let ops = context.operations();
537 assert!(ops.contains("\\(parentheses\\)"));
539 assert!(ops.contains("\\\\backslash\\\\"));
540 }
541
542 #[test]
543 fn test_write_wrapped_newlines_tabs() {
544 let margins = create_test_margins();
545 let mut context = TextFlowContext::new(400.0, 600.0, margins);
546
547 context.write_wrapped("Line1\nLine2\tTabbed").unwrap();
548
549 let ops = context.operations();
550 assert!(ops.contains("\\n") || ops.contains("\\t"));
552 }
553
554 #[test]
555 fn test_write_wrapped_very_long_word() {
556 let margins = create_test_margins();
557 let mut context = TextFlowContext::new(200.0, 600.0, margins); let long_word = "a".repeat(100);
560 context.write_wrapped(&long_word).unwrap();
561
562 let ops = context.operations();
563 assert!(ops.contains("BT\n"));
564 assert!(ops.contains(&long_word));
565 }
566
567 #[test]
568 fn test_write_wrapped_cursor_movement() {
569 let margins = create_test_margins();
570 let mut context = TextFlowContext::new(400.0, 600.0, margins);
571
572 let initial_y = context.cursor_y;
573
574 context.write_wrapped("Line 1").unwrap();
575 let y_after_line1 = context.cursor_y;
576
577 context.write_wrapped("Line 2").unwrap();
578 let y_after_line2 = context.cursor_y;
579
580 assert!(y_after_line1 < initial_y);
582 assert!(y_after_line2 < y_after_line1);
583 }
584
585 #[test]
586 fn test_write_paragraph_spacing() {
587 let margins = create_test_margins();
588 let mut context = TextFlowContext::new(400.0, 600.0, margins);
589
590 let initial_y = context.cursor_y;
591 context.write_paragraph("Paragraph 1").unwrap();
592 let y_after_p1 = context.cursor_y;
593
594 context.write_paragraph("Paragraph 2").unwrap();
595 let y_after_p2 = context.cursor_y;
596
597 let spacing1 = initial_y - y_after_p1;
599 let spacing2 = y_after_p1 - y_after_p2;
600
601 assert!(spacing1 > 0.0);
602 assert!(spacing2 > 0.0);
603 }
604
605 #[test]
606 fn test_multiple_newlines() {
607 let margins = create_test_margins();
608 let mut context = TextFlowContext::new(400.0, 600.0, margins);
609
610 let initial_y = context.cursor_y;
611
612 context.newline();
613 let y1 = context.cursor_y;
614
615 context.newline();
616 let y2 = context.cursor_y;
617
618 context.newline();
619 let y3 = context.cursor_y;
620
621 let spacing1 = initial_y - y1;
623 let spacing2 = y1 - y2;
624 let spacing3 = y2 - y3;
625
626 assert!((spacing1 - spacing2).abs() < 1e-10);
628 assert!((spacing2 - spacing3).abs() < 1e-10);
629 assert!((spacing1 - context.font_size * context.line_height).abs() < 1e-10);
630 }
631
632 #[test]
633 fn test_content_width_different_margins() {
634 let margins = Margins {
635 left: 30.0,
636 right: 70.0,
637 top: 40.0,
638 bottom: 60.0,
639 };
640 let context = TextFlowContext::new(500.0, 700.0, margins);
641
642 let content_width = context.content_width();
643 assert_eq!(content_width, 400.0); }
645
646 #[test]
647 fn test_custom_line_height() {
648 let margins = create_test_margins();
649 let mut context = TextFlowContext::new(400.0, 600.0, margins);
650
651 context.set_line_height(2.0);
652
653 let initial_y = context.cursor_y;
654 context.newline();
655 let y_after = context.cursor_y;
656
657 let spacing = initial_y - y_after;
658 assert_eq!(spacing, context.font_size * 2.0); }
660
661 #[test]
662 fn test_different_fonts() {
663 let margins = create_test_margins();
664 let mut context = TextFlowContext::new(400.0, 600.0, margins);
665
666 let fonts = vec![
667 Font::Helvetica,
668 Font::HelveticaBold,
669 Font::TimesRoman,
670 Font::TimesBold,
671 Font::Courier,
672 Font::CourierBold,
673 ];
674
675 for font in fonts {
676 context.clear();
677 let font_name = font.pdf_name();
678 context.set_font(font, 14.0);
679 context.write_wrapped("Test text").unwrap();
680
681 let ops = context.operations();
682 assert!(ops.contains(&format!("/{font_name} 14 Tf")));
683 }
684 }
685
686 #[test]
687 fn test_font_size_variations() {
688 let margins = create_test_margins();
689 let mut context = TextFlowContext::new(400.0, 600.0, margins);
690
691 let sizes = vec![8.0, 10.0, 12.0, 14.0, 16.0, 24.0, 36.0];
692
693 for size in sizes {
694 context.clear();
695 context.set_font(Font::Helvetica, size);
696 context.write_wrapped("Test").unwrap();
697
698 let ops = context.operations();
699 assert!(ops.contains(&format!("/Helvetica {size} Tf")));
700 }
701 }
702
703 #[test]
704 fn test_at_position_edge_cases() {
705 let margins = create_test_margins();
706 let mut context = TextFlowContext::new(400.0, 600.0, margins);
707
708 context.at(0.0, 0.0);
710 assert_eq!(context.cursor_position(), (0.0, 0.0));
711
712 context.at(-10.0, -20.0);
714 assert_eq!(context.cursor_position(), (-10.0, -20.0));
715
716 context.at(10000.0, 20000.0);
718 assert_eq!(context.cursor_position(), (10000.0, 20000.0));
719 }
720
721 #[test]
722 fn test_write_wrapped_with_narrow_content() {
723 let margins = Margins {
724 left: 190.0,
725 right: 190.0,
726 top: 50.0,
727 bottom: 50.0,
728 };
729 let mut context = TextFlowContext::new(400.0, 600.0, margins);
730
731 context
733 .write_wrapped("This text should wrap a lot")
734 .unwrap();
735
736 let ops = context.operations();
737 let bt_count = ops.matches("BT\n").count();
739 assert!(bt_count > 1);
740 }
741
742 #[test]
743 fn test_justified_text_single_word_line() {
744 let margins = create_test_margins();
745 let mut context = TextFlowContext::new(400.0, 600.0, margins);
746
747 context.set_alignment(TextAlign::Justified);
748 context.write_wrapped("SingleWord").unwrap();
749
750 let ops = context.operations();
751 assert!(!ops.contains(" Tw") || ops.contains("0 Tw"));
753 }
754
755 #[test]
756 fn test_justified_text_last_line() {
757 let margins = create_test_margins();
758 let mut context = TextFlowContext::new(400.0, 600.0, margins);
759
760 context.set_alignment(TextAlign::Justified);
761 context.write_wrapped("This is a test of justified text alignment where the last line should not be justified").unwrap();
763
764 let ops = context.operations();
765 assert!(ops.contains("0 Tw"));
767 }
768
769 #[test]
770 fn test_generate_operations_encoding() {
771 let margins = create_test_margins();
772 let mut context = TextFlowContext::new(400.0, 600.0, margins);
773
774 context.write_wrapped("UTF-8 Text: Ñ").unwrap();
775
776 let ops_bytes = context.generate_operations();
777 let ops_string = String::from_utf8(ops_bytes.clone()).unwrap();
778
779 assert_eq!(ops_bytes, context.operations().as_bytes());
780 assert_eq!(ops_string, context.operations());
781 }
782
783 #[test]
784 fn test_clear_resets_operations_only() {
785 let margins = create_test_margins();
786 let mut context = TextFlowContext::new(400.0, 600.0, margins);
787
788 context.set_font(Font::TimesBold, 18.0);
789 context.set_alignment(TextAlign::Right);
790 context.at(100.0, 200.0);
791 context.write_wrapped("Text").unwrap();
792
793 context.clear();
794
795 assert!(context.operations().is_empty());
797
798 assert_eq!(context.current_font, Font::TimesBold);
800 assert_eq!(context.font_size, 18.0);
801 assert_eq!(context.alignment(), TextAlign::Right);
802 let (x, y) = context.cursor_position();
804 assert_eq!(x, 100.0); assert!(y < 200.0); }
807
808 #[test]
809 fn test_long_text_wrapping() {
810 let margins = create_test_margins();
811 let mut context = TextFlowContext::new(400.0, 600.0, margins);
812
813 let long_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
814 Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \
815 Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.";
816
817 context.write_wrapped(long_text).unwrap();
818
819 let ops = context.operations();
820 let tj_count = ops.matches(") Tj").count();
822 assert!(tj_count > 1);
823 }
824
825 #[test]
826 fn test_empty_operations_initially() {
827 let margins = create_test_margins();
828 let context = TextFlowContext::new(400.0, 600.0, margins);
829
830 assert!(context.operations().is_empty());
831 assert_eq!(context.generate_operations().len(), 0);
832 }
833
834 #[test]
835 fn test_write_paragraph_empty() {
836 let margins = create_test_margins();
837 let mut context = TextFlowContext::new(400.0, 600.0, margins);
838
839 let initial_y = context.cursor_y;
840 context.write_paragraph("").unwrap();
841
842 assert!(context.cursor_y < initial_y);
844 }
845
846 #[test]
847 fn test_extreme_line_height() {
848 let margins = create_test_margins();
849 let mut context = TextFlowContext::new(400.0, 600.0, margins);
850
851 context.set_line_height(0.1);
853 let initial_y = context.cursor_y;
854 context.newline();
855 assert_eq!(context.cursor_y, initial_y - context.font_size * 0.1);
856
857 context.set_line_height(10.0);
859 let initial_y2 = context.cursor_y;
860 context.newline();
861 assert_eq!(context.cursor_y, initial_y2 - context.font_size * 10.0);
862 }
863
864 #[test]
865 fn test_zero_content_width() {
866 let margins = Margins {
867 left: 200.0,
868 right: 200.0,
869 top: 50.0,
870 bottom: 50.0,
871 };
872 let context = TextFlowContext::new(400.0, 600.0, margins);
873
874 assert_eq!(context.content_width(), 0.0);
875 }
876
877 #[test]
878 fn test_cursor_x_reset_on_newline() {
879 let margins = create_test_margins();
880 let mut context = TextFlowContext::new(400.0, 600.0, margins.clone());
881
882 context.at(250.0, 300.0); context.newline();
884
885 assert_eq!(context.cursor_x, margins.left);
887 assert_eq!(
889 context.cursor_y,
890 300.0 - context.font_size * context.line_height
891 );
892 }
893}