add tests and readme
This commit is contained in:
@@ -0,0 +1,659 @@
|
||||
//! Integration tests for pdf-to-markdown library
|
||||
|
||||
use pdf_to_markdown::{
|
||||
detect_pdf_type, extract_text, extract_text_with_positions, to_markdown,
|
||||
MarkdownOptions, PdfType, TextItem,
|
||||
};
|
||||
use pdf_to_markdown::detector::DetectionConfig;
|
||||
use pdf_to_markdown::extractor::{group_into_lines, TextLine};
|
||||
|
||||
// Helper to create test TextItems
|
||||
fn make_text_item(text: &str, x: f32, y: f32, font_size: f32, page: u32) -> TextItem {
|
||||
TextItem {
|
||||
text: text.to_string(),
|
||||
x,
|
||||
y,
|
||||
width: text.len() as f32 * font_size * 0.5,
|
||||
height: font_size,
|
||||
font: "Helvetica".to_string(),
|
||||
font_size,
|
||||
page,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_text_item_with_font(text: &str, x: f32, y: f32, font_size: f32, font: &str, page: u32) -> TextItem {
|
||||
TextItem {
|
||||
text: text.to_string(),
|
||||
x,
|
||||
y,
|
||||
width: text.len() as f32 * font_size * 0.5,
|
||||
height: font_size,
|
||||
font: font.to_string(),
|
||||
font_size,
|
||||
page,
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Detection Config Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_detection_config_default() {
|
||||
let config = DetectionConfig::default();
|
||||
assert_eq!(config.max_pages_to_sample, 5);
|
||||
assert_eq!(config.min_text_ops_per_page, 3);
|
||||
assert!((config.text_page_ratio_threshold - 0.6).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detection_config_custom() {
|
||||
let config = DetectionConfig {
|
||||
max_pages_to_sample: 10,
|
||||
min_text_ops_per_page: 5,
|
||||
text_page_ratio_threshold: 0.8,
|
||||
};
|
||||
assert_eq!(config.max_pages_to_sample, 10);
|
||||
assert_eq!(config.min_text_ops_per_page, 5);
|
||||
assert!((config.text_page_ratio_threshold - 0.8).abs() < 0.001);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PdfType Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_pdf_type_equality() {
|
||||
assert_eq!(PdfType::TextBased, PdfType::TextBased);
|
||||
assert_eq!(PdfType::Scanned, PdfType::Scanned);
|
||||
assert_eq!(PdfType::ImageBased, PdfType::ImageBased);
|
||||
assert_eq!(PdfType::Mixed, PdfType::Mixed);
|
||||
assert_ne!(PdfType::TextBased, PdfType::Scanned);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pdf_type_clone() {
|
||||
let original = PdfType::TextBased;
|
||||
let cloned = original.clone();
|
||||
assert_eq!(original, cloned);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pdf_type_debug() {
|
||||
let pdf_type = PdfType::TextBased;
|
||||
let debug_str = format!("{:?}", pdf_type);
|
||||
assert_eq!(debug_str, "TextBased");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TextItem Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_text_item_creation() {
|
||||
let item = make_text_item("Hello", 100.0, 700.0, 12.0, 1);
|
||||
assert_eq!(item.text, "Hello");
|
||||
assert_eq!(item.x, 100.0);
|
||||
assert_eq!(item.y, 700.0);
|
||||
assert_eq!(item.font_size, 12.0);
|
||||
assert_eq!(item.page, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_text_item_clone() {
|
||||
let item = make_text_item("Test", 50.0, 600.0, 14.0, 2);
|
||||
let cloned = item.clone();
|
||||
assert_eq!(item.text, cloned.text);
|
||||
assert_eq!(item.x, cloned.x);
|
||||
assert_eq!(item.y, cloned.y);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TextLine Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_text_line_text_method() {
|
||||
let items = vec![
|
||||
make_text_item("Hello", 100.0, 700.0, 12.0, 1),
|
||||
make_text_item("World", 160.0, 700.0, 12.0, 1),
|
||||
];
|
||||
let line = TextLine {
|
||||
items,
|
||||
y: 700.0,
|
||||
page: 1,
|
||||
};
|
||||
assert_eq!(line.text(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_text_line_single_item() {
|
||||
let items = vec![make_text_item("Single", 100.0, 700.0, 12.0, 1)];
|
||||
let line = TextLine {
|
||||
items,
|
||||
y: 700.0,
|
||||
page: 1,
|
||||
};
|
||||
assert_eq!(line.text(), "Single");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_text_line_empty() {
|
||||
let line = TextLine {
|
||||
items: vec![],
|
||||
y: 700.0,
|
||||
page: 1,
|
||||
};
|
||||
assert_eq!(line.text(), "");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Group Into Lines Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_group_into_lines_empty() {
|
||||
let items: Vec<TextItem> = vec![];
|
||||
let lines = group_into_lines(items);
|
||||
assert!(lines.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_group_into_lines_same_line() {
|
||||
let items = vec![
|
||||
make_text_item("A", 100.0, 700.0, 12.0, 1),
|
||||
make_text_item("B", 120.0, 700.0, 12.0, 1),
|
||||
make_text_item("C", 140.0, 700.0, 12.0, 1),
|
||||
];
|
||||
let lines = group_into_lines(items);
|
||||
assert_eq!(lines.len(), 1);
|
||||
assert_eq!(lines[0].items.len(), 3);
|
||||
assert_eq!(lines[0].text(), "A B C");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_group_into_lines_different_lines() {
|
||||
let items = vec![
|
||||
make_text_item("Line1", 100.0, 700.0, 12.0, 1),
|
||||
make_text_item("Line2", 100.0, 680.0, 12.0, 1),
|
||||
make_text_item("Line3", 100.0, 660.0, 12.0, 1),
|
||||
];
|
||||
let lines = group_into_lines(items);
|
||||
assert_eq!(lines.len(), 3);
|
||||
assert_eq!(lines[0].text(), "Line1");
|
||||
assert_eq!(lines[1].text(), "Line2");
|
||||
assert_eq!(lines[2].text(), "Line3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_group_into_lines_y_tolerance() {
|
||||
// Items within 3.0 Y tolerance should be grouped
|
||||
// Note: items are sorted by Y descending, then X ascending
|
||||
let items = vec![
|
||||
make_text_item("A", 100.0, 700.0, 12.0, 1),
|
||||
make_text_item("B", 150.0, 700.0, 12.0, 1), // Same Y
|
||||
];
|
||||
let lines = group_into_lines(items);
|
||||
assert_eq!(lines.len(), 1);
|
||||
assert_eq!(lines[0].text(), "A B");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_group_into_lines_multiple_pages() {
|
||||
let items = vec![
|
||||
make_text_item("Page1Text", 100.0, 700.0, 12.0, 1),
|
||||
make_text_item("Page2Text", 100.0, 700.0, 12.0, 2),
|
||||
];
|
||||
let lines = group_into_lines(items);
|
||||
assert_eq!(lines.len(), 2);
|
||||
assert_eq!(lines[0].page, 1);
|
||||
assert_eq!(lines[1].page, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_group_into_lines_sorting_by_x() {
|
||||
// Items on same line should be sorted by X position
|
||||
let items = vec![
|
||||
make_text_item("Third", 200.0, 700.0, 12.0, 1),
|
||||
make_text_item("First", 50.0, 700.0, 12.0, 1),
|
||||
make_text_item("Second", 100.0, 700.0, 12.0, 1),
|
||||
];
|
||||
let lines = group_into_lines(items);
|
||||
assert_eq!(lines.len(), 1);
|
||||
assert_eq!(lines[0].text(), "First Second Third");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// MarkdownOptions Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_markdown_options_default() {
|
||||
let opts = MarkdownOptions::default();
|
||||
assert!(opts.detect_headers);
|
||||
assert!(opts.detect_lists);
|
||||
assert!(opts.detect_code);
|
||||
assert!(opts.base_font_size.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_markdown_options_custom() {
|
||||
let opts = MarkdownOptions {
|
||||
detect_headers: false,
|
||||
detect_lists: true,
|
||||
detect_code: false,
|
||||
base_font_size: Some(14.0),
|
||||
};
|
||||
assert!(!opts.detect_headers);
|
||||
assert!(opts.detect_lists);
|
||||
assert!(!opts.detect_code);
|
||||
assert_eq!(opts.base_font_size, Some(14.0));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Markdown Conversion Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_basic() {
|
||||
let text = "Hello World";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.contains("Hello World"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_multiple_lines() {
|
||||
let text = "Line one\nLine two\nLine three";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.contains("Line one"));
|
||||
assert!(md.contains("Line two"));
|
||||
assert!(md.contains("Line three"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_bullet_list() {
|
||||
let text = "• First\n• Second\n• Third";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.contains("- First"));
|
||||
assert!(md.contains("- Second"));
|
||||
assert!(md.contains("- Third"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_dash_list() {
|
||||
let text = "- One\n- Two\n- Three";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.contains("- One"));
|
||||
assert!(md.contains("- Two"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_numbered_list() {
|
||||
let text = "1. First\n2. Second\n3. Third";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.contains("1. First"));
|
||||
assert!(md.contains("2. Second"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_code_detection() {
|
||||
let text = "const x = 5;\nlet y = 10;";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.contains("```"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_no_code_detection() {
|
||||
let text = "const x = 5;";
|
||||
let opts = MarkdownOptions {
|
||||
detect_code: false,
|
||||
..Default::default()
|
||||
};
|
||||
let md = to_markdown(text, opts);
|
||||
assert!(!md.contains("```"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_no_list_detection() {
|
||||
let text = "• Item";
|
||||
let opts = MarkdownOptions {
|
||||
detect_lists: false,
|
||||
..Default::default()
|
||||
};
|
||||
let md = to_markdown(text, opts);
|
||||
// Should keep original bullet character
|
||||
assert!(md.contains("•"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_empty_lines() {
|
||||
let text = "Para one\n\nPara two";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.contains("Para one"));
|
||||
assert!(md.contains("Para two"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_markdown_whitespace_only_lines() {
|
||||
let text = "Content\n \nMore content";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.contains("Content"));
|
||||
assert!(md.contains("More content"));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Markdown From Items Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_markdown_from_items_empty() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
let items: Vec<TextItem> = vec![];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_markdown_from_items_single() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
let items = vec![make_text_item("Hello", 100.0, 700.0, 12.0, 1)];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("Hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_markdown_from_items_header_detection() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
// Need multiple body items to establish base font size
|
||||
let items = vec![
|
||||
make_text_item("Title", 100.0, 750.0, 24.0, 1), // Large font = H1
|
||||
make_text_item("Body text one", 100.0, 700.0, 12.0, 1),
|
||||
make_text_item("Body text two", 100.0, 680.0, 12.0, 1),
|
||||
make_text_item("Body text three", 100.0, 660.0, 12.0, 1),
|
||||
];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("# Title"));
|
||||
assert!(md.contains("Body text"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_markdown_from_items_h2_detection() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
let items = vec![
|
||||
make_text_item("Subtitle", 100.0, 750.0, 18.0, 1), // 1.5x = H2
|
||||
make_text_item("Body text", 100.0, 700.0, 12.0, 1),
|
||||
];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("## Subtitle"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_markdown_from_items_monospace_code() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
let items = vec![
|
||||
make_text_item_with_font("let x = 5", 100.0, 700.0, 12.0, "Courier", 1),
|
||||
];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("```"));
|
||||
assert!(md.contains("let x = 5"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_markdown_from_items_page_breaks() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
let items = vec![
|
||||
make_text_item("Page 1", 100.0, 700.0, 12.0, 1),
|
||||
make_text_item("Page 2", 100.0, 700.0, 12.0, 2),
|
||||
];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("---")); // Page break marker
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Markdown From Lines Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_markdown_from_lines_empty() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_lines;
|
||||
let lines: Vec<TextLine> = vec![];
|
||||
let md = to_markdown_from_lines(lines, MarkdownOptions::default());
|
||||
assert!(md.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_markdown_from_lines_basic() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_lines;
|
||||
let lines = vec![
|
||||
TextLine {
|
||||
items: vec![make_text_item("First", 100.0, 700.0, 12.0, 1)],
|
||||
y: 700.0,
|
||||
page: 1,
|
||||
},
|
||||
TextLine {
|
||||
items: vec![make_text_item("Second", 100.0, 680.0, 12.0, 1)],
|
||||
y: 680.0,
|
||||
page: 1,
|
||||
},
|
||||
];
|
||||
let md = to_markdown_from_lines(lines, MarkdownOptions::default());
|
||||
assert!(md.contains("First"));
|
||||
assert!(md.contains("Second"));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Error Handling Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_extract_text_nonexistent_file() {
|
||||
let result = extract_text("/nonexistent/file.pdf");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_pdf_type_nonexistent_file() {
|
||||
let result = detect_pdf_type("/nonexistent/file.pdf");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_text_with_positions_nonexistent_file() {
|
||||
let result = extract_text_with_positions("/nonexistent/file.pdf");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// List Pattern Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_bullet_variations() {
|
||||
// Unicode bullets get converted to markdown dash
|
||||
let unicode_bullets = ["• Item", "○ Item", "● Item", "◦ Item"];
|
||||
for bullet in &unicode_bullets {
|
||||
let md = to_markdown(bullet, MarkdownOptions::default());
|
||||
assert!(md.contains("- Item"), "Failed for: {}", bullet);
|
||||
}
|
||||
|
||||
// Markdown-compatible bullets stay as-is
|
||||
let md_bullets = ["- Item", "* Item"];
|
||||
for bullet in &md_bullets {
|
||||
let md = to_markdown(bullet, MarkdownOptions::default());
|
||||
assert!(md.contains(bullet), "Failed for: {}", bullet);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_numbered_list_variations() {
|
||||
let lists = ["1. First", "2) Second", "10. Tenth"];
|
||||
for item in &lists {
|
||||
let md = to_markdown(item, MarkdownOptions::default());
|
||||
assert!(md.trim().len() > 0, "Failed for: {}", item);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_letter_list_items() {
|
||||
let md = to_markdown("a. Letter item", MarkdownOptions::default());
|
||||
assert!(md.contains("a. Letter item"));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Code Detection Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_code_keywords() {
|
||||
let keywords = [
|
||||
"import foo",
|
||||
"export default",
|
||||
"const x = 5;",
|
||||
"let y = 10;",
|
||||
"function test() {",
|
||||
"class MyClass {",
|
||||
"def func():",
|
||||
"pub fn main() {",
|
||||
"async fn process() {",
|
||||
"impl Trait {",
|
||||
];
|
||||
for code in &keywords {
|
||||
let md = to_markdown(code, MarkdownOptions::default());
|
||||
assert!(md.contains("```"), "Code not detected for: {}", code);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_syntax_patterns() {
|
||||
// Patterns that start with code keywords/syntax
|
||||
let patterns = [
|
||||
"=> value", // Starts with =>
|
||||
"-> Result", // Starts with ->
|
||||
":: io::Result", // Starts with ::
|
||||
];
|
||||
for code in &patterns {
|
||||
let md = to_markdown(code, MarkdownOptions::default());
|
||||
assert!(md.contains("```"), "Code not detected for: {}", code);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_code_special_chars() {
|
||||
let code = "if (x > 0) { return y; }";
|
||||
let md = to_markdown(code, MarkdownOptions::default());
|
||||
assert!(md.contains("```"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_code_text() {
|
||||
let text = "This is regular text about programming.";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(!md.contains("```"));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Monospace Font Detection Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_monospace_font_names() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
// Font names that contain the patterns in is_monospace_font
|
||||
let monospace_fonts = [
|
||||
"Courier", "Consolas", "Monaco", "Menlo",
|
||||
"Fira Code", "JetBrains Mono", "Inconsolata",
|
||||
"DejaVu Sans Mono", "Liberation Mono", "Fixed", "Terminal",
|
||||
];
|
||||
|
||||
for font in &monospace_fonts {
|
||||
let items = vec![make_text_item_with_font("code", 100.0, 700.0, 12.0, font, 1)];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("```"), "Font not detected as monospace: {}", font);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Header Level Detection Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_header_level_h1() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
// 24.0 / 12.0 = 2.0x = H1
|
||||
// Need multiple body items to establish base font size
|
||||
let items = vec![
|
||||
make_text_item("H1 Title", 100.0, 700.0, 24.0, 1),
|
||||
make_text_item("body text one", 100.0, 650.0, 12.0, 1),
|
||||
make_text_item("body text two", 100.0, 630.0, 12.0, 1),
|
||||
make_text_item("body text three", 100.0, 610.0, 12.0, 1),
|
||||
];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("# H1 Title"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_level_h2() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
// 18.0 / 12.0 = 1.5x = H2
|
||||
// Need multiple body items to establish base font size
|
||||
let items = vec![
|
||||
make_text_item("H2 Title", 100.0, 700.0, 18.0, 1),
|
||||
make_text_item("body text one", 100.0, 650.0, 12.0, 1),
|
||||
make_text_item("body text two", 100.0, 630.0, 12.0, 1),
|
||||
make_text_item("body text three", 100.0, 610.0, 12.0, 1),
|
||||
];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("## H2 Title"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_level_h3() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
// 15.0 / 12.0 = 1.25x = H3
|
||||
// Need multiple body items to establish base font size
|
||||
let items = vec![
|
||||
make_text_item("H3 Title", 100.0, 700.0, 15.0, 1),
|
||||
make_text_item("body text one", 100.0, 650.0, 12.0, 1),
|
||||
make_text_item("body text two", 100.0, 630.0, 12.0, 1),
|
||||
make_text_item("body text three", 100.0, 610.0, 12.0, 1),
|
||||
];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("### H3 Title"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_level_h4() {
|
||||
use pdf_to_markdown::markdown::to_markdown_from_items;
|
||||
// 13.5 / 12.0 = 1.125x = H4 (>= 1.1)
|
||||
// Need multiple body items to establish base font size
|
||||
let items = vec![
|
||||
make_text_item("H4 Title", 100.0, 700.0, 13.5, 1),
|
||||
make_text_item("body text one", 100.0, 650.0, 12.0, 1),
|
||||
make_text_item("body text two", 100.0, 630.0, 12.0, 1),
|
||||
make_text_item("body text three", 100.0, 610.0, 12.0, 1),
|
||||
];
|
||||
let md = to_markdown_from_items(items, MarkdownOptions::default());
|
||||
assert!(md.contains("#### H4 Title"));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Clean Markdown Tests
|
||||
// ============================================================================
|
||||
|
||||
#[test]
|
||||
fn test_excessive_newlines_preserved_in_plain_text() {
|
||||
// Plain text to_markdown preserves structure from input
|
||||
let text = "Para one\n\n\n\n\nPara two";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
// The function processes line by line, empty lines become single newlines
|
||||
assert!(md.contains("Para one"));
|
||||
assert!(md.contains("Para two"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailing_newline() {
|
||||
let text = "Content";
|
||||
let md = to_markdown(text, MarkdownOptions::default());
|
||||
assert!(md.ends_with('\n'));
|
||||
assert!(!md.ends_with("\n\n"));
|
||||
}
|
||||
Reference in New Issue
Block a user