use crate::common::{diag_output_dir, ensure_test_image};
use liepress::generator::markdown_to_document;
use liepress::visual::VisualElement;
use liepress::{ConvertOptions, markdown_to_pdf};
use std::fs;
use std::path::{Path, PathBuf};
fn output_dir_markdown_to_svg(md: &str, output_dir: &Path, name: &str) {
use liepress::PageRenderer;
use liepress::generator::markdown_to_document;
use liepress::render::SvgRenderer;
let doc = markdown_to_document(md);
for (i, page) in doc.pages.iter().enumerate() {
let mut renderer = SvgRenderer::new(page.width, page.height);
renderer.render_elements(&page.elements);
let svg = renderer.finalize();
let path = output_dir.join(format!("{}_{}.svg", name, i));
fs::write(&path, &svg).expect("Should write SVG file");
println!("SVG saved to: {}", path.display());
}
}
fn output_dir_markdown_to_pdf(md: &str, output_dir: &Path, name: &str) {
let pdf_data =
markdown_to_pdf(md, &ConvertOptions::default()).expect("PDF generation should succeed");
let path = output_dir.join(format!("{}.pdf", name));
fs::write(&path, &pdf_data).expect("Should write PDF file");
println!("PDF saved to: {}", path.display());
}
#[test]
fn test_code_block_diagnostic() {
let output_dir = diag_output_dir("code_block_diag");
let md = r#"# Test
Some text before.
```
fn main() {
println!("hello");
}
```
Some text after."#;
output_dir_markdown_to_svg(md, &output_dir, "code_block");
output_dir_markdown_to_pdf(md, &output_dir, "code_block");
}
#[test]
fn test_list_diagnostic() {
let output_dir = diag_output_dir("list_diag");
let md = r#"# List Test
Unordered list:
- Item 1
- Item 2
- Sub 2.1
- Sub 2.2
- Item 3
Ordered list:
1. First
2. Second
3. Third"#;
output_dir_markdown_to_svg(md, &output_dir, "list");
output_dir_markdown_to_pdf(md, &output_dir, "list");
}
#[test]
fn test_pagination_diagnostic() {
let output_dir = diag_output_dir("pagination_diag");
let mut md = String::from("# Pagination Test\n\n");
for i in 0..50 {
md.push_str(&format!(
"Paragraph {} with enough text to test pagination across multiple pages. ",
i
));
md.push_str("Lorem ipsum dolor sit amet, consectetur adipiscing elit. ");
md.push_str("Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\n");
}
output_dir_markdown_to_svg(&md, &output_dir, "pagination");
output_dir_markdown_to_pdf(&md, &output_dir, "pagination");
}
#[test]
fn test_image_diagnostic() {
let output_dir = diag_output_dir("image_diag");
let md = r#"# Image Test
Text before image.

Text after image."#;
output_dir_markdown_to_svg(md, &output_dir, "image");
output_dir_markdown_to_pdf(md, &output_dir, "image");
}
#[test]
fn test_full_featured_diagnostic() {
let output_dir = diag_output_dir("full_featured");
let md = r#"# Complete Document
## Typography
Regular, **bold**, *italic*, and `code` text.
## Lists
### Unordered
- Item A
- Item B
- Item C
### Ordered
1. First
2. Second
3. Third
## Code Block
```rust
fn main() {
println!("Hello, world!");
}
```
## Blockquote
> A famous quote goes here.
> It can span multiple lines.
## Thematic Break
---
## Link
Visit [Rust](https://www.rust-lang.org/) for more information."#;
output_dir_markdown_to_svg(md, &output_dir, "full_featured");
output_dir_markdown_to_pdf(md, &output_dir, "full_featured");
}
#[test]
fn test_image_example_diagnostic() {
let fixtures_path = PathBuf::from("tests/fixtures/test_image.png");
ensure_test_image(&fixtures_path);
let output_dir = diag_output_dir("image_example_diag");
let md = crate::common::samples::IMAGE_EXAMPLE;
output_dir_markdown_to_svg(md, &output_dir, "image_example");
output_dir_markdown_to_pdf(md, &output_dir, "image_example");
}
#[test]
fn test_center_tag_diagnostic() {
let output_dir = diag_output_dir("center_tag_diag");
let md_block = r#"<center>
# LiePress
</center>
# 安装
test"#;
output_dir_markdown_to_svg(md_block, &output_dir, "center_tag_block");
output_dir_markdown_to_pdf(md_block, &output_dir, "center_tag_block");
let md_inline = r#"<center>#LiePress</center>
# 安装
test"#;
output_dir_markdown_to_svg(md_inline, &output_dir, "center_tag_inline");
output_dir_markdown_to_pdf(md_inline, &output_dir, "center_tag_inline");
}
#[test]
fn test_tasklist_debug_elements() {
let md = "- [ ] 未勾选\n- [x] 已勾选\n\n- Regular bullet item";
let doc = markdown_to_document(md);
eprintln!("=== Task List Debug ===");
for (pi, page) in doc.pages.iter().enumerate() {
eprintln!("Page {}: {} elements", pi, page.elements.len());
for (ei, elem) in page.elements.iter().enumerate() {
match elem {
VisualElement::TextLine {
runs,
bounds,
line_height,
} => {
let text_info: Vec<String> = runs
.iter()
.map(|r| {
let glyph_info: Vec<String> =
r.glyphs.iter().map(|g| format!("id={}", g.id)).collect();
format!(
"text={:?} gl={} adv={:.1} glyphs=[{}]",
r.text,
r.glyphs.len(),
r.advance,
glyph_info.join(",")
)
})
.collect();
eprintln!(
" [{}] TextLine b=({:.0},{:.0})-({:.0},{:.0}) lh={} {}",
ei,
bounds.x0,
bounds.y0,
bounds.x1,
bounds.y1,
line_height,
text_info.join(" | ")
);
}
_ => eprintln!(" [{}] {:?}", ei, elem),
}
}
}
assert!(doc.pages[0].elements.len() >= 2);
}
#[test]
fn test_marker_char_glyphs() {
let test_chars = [
("☐", "U+2610 BALLOT BOX"),
("☑", "U+2611 BALLOT BOX WITH CHECK"),
("☒", "U+2612 BALLOT BOX WITH X"),
("□", "U+25A1 WHITE SQUARE"),
("■", "U+25A0 BLACK SQUARE"),
("○", "U+25CB WHITE CIRCLE"),
("●", "U+25CF BLACK CIRCLE"),
("•", "U+2022 BULLET"),
("[ ]", "ASCII [ ]"),
("[x]", "ASCII [x]"),
("✓", "U+2713 CHECK MARK"),
("✔", "U+2714 HEAVY CHECK MARK"),
("✗", "U+2717 BALLOT X"),
("✘", "U+2718 HEAVY BALLOT X"),
("⬜", "U+2B1C WHITE LARGE SQUARE"),
("⬛", "U+2B1B BLACK LARGE SQUARE"),
("✅", "U+2705 WHITE HEAVY CHECK MARK"),
("❌", "U+274C CROSS MARK"),
("❎", "U+274E NEGATIVE SQUARED CROSS MARK"),
("🗹", "U+1F5F9 BALLOT BOX WITH BOLD CHECK"),
("🗸", "U+1F5F8 LIGHT CHECK MARK"),
];
let marker_style = liepress::ast::list_marker_style();
let text_style = liepress::ast::computed_style_to_text_style(&marker_style);
eprintln!("=== Marker Character Glyph Test ===");
for (ch, desc) in &test_chars {
let layout = liepress::text::create_text_layout(ch, &text_style, None);
for line in &layout.lines {
for run in &line.runs {
let ids: Vec<u32> = run.glyphs.iter().map(|g| g.id).collect();
let adv = run.advance;
eprintln!(
"{} ({:>8}): glyph_ids=[{}], advance={:.1}",
desc,
ch,
ids.iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(","),
adv
);
}
}
}
}
#[test]
fn diag_trace_url() {
use liepress::generator::markdown_to_document;
use liepress::visual::VisualElement;
let md1 = r#"[Example](http://example.com)"#;
let doc1 = markdown_to_document(md1);
println!("=== Simple link ===");
for page in &doc1.pages {
for elem in &page.elements {
if let VisualElement::TextLine { runs, bounds, .. } = elem {
println!(" TextLine bounds={:?}", bounds);
for (ri, run) in runs.iter().enumerate() {
println!(
" Run[{}]: text={:?}, url={:?}, baseline_x={}, advance={}, font_size={}",
ri, run.text, run.url, run.baseline_x, run.advance, run.font_size
);
}
}
}
}
let md2 = r#"This is a paragraph with a [link](http://example.com) in the middle."#;
let doc2 = markdown_to_document(md2);
println!("=== Link in paragraph ===");
for page in &doc2.pages {
for elem in &page.elements {
if let VisualElement::TextLine { runs, bounds, .. } = elem {
println!(" TextLine bounds={:?}", bounds);
for (ri, run) in runs.iter().enumerate() {
println!(
" Run[{}]: text={:?}, url={:?}, baseline_x={}, advance={}, font_size={}",
ri, run.text, run.url, run.baseline_x, run.advance, run.font_size
);
}
}
}
}
}