# HTML Rendering
This guide explains how HTML rendering works in `pulldown-html-ext` and how to customize the rendering process.
## Core Concepts
The HTML rendering system consists of several key components:
1. `HtmlRenderer` - The core renderer that processes Markdown events
2. `HtmlWriter` - The trait that defines how elements are rendered
3. `HtmlState` - Maintains state during rendering
4. `DefaultHtmlWriter` - The default implementation of `HtmlWriter`
## Basic Rendering
The simplest way to render Markdown to HTML:
```rust
use pulldown_cmark::Parser;
use pulldown_html_ext::{HtmlConfig, push_html};
let config = HtmlConfig::default();
let markdown = "# Hello\nThis is *markdown*";
let parser = Parser::new(markdown);
let mut output = String::new();
push_html(&mut output, parser, &config)?;
```
## Working with the Renderer Directly
For more control, you can work with the renderer directly:
```rust
use pulldown_html_ext::{create_html_renderer, DefaultHtmlWriter};
use pulldown_cmark::{Parser, Event};
use pulldown_cmark_escape::FmtWriter;
let mut output = String::new();
let writer = DefaultHtmlWriter::new(FmtWriter(&mut output), &config);
let mut renderer = create_html_renderer(writer);
let parser = Parser::new(markdown);
renderer.run(parser)?;
```
## State Management
The renderer maintains state during processing:
```rust
pub struct HtmlState {
pub numbers: Vec, // For ordered lists
pub table_state: TableContext, // Current table state
pub table_cell_index: usize, // Current cell in table
pub table_alignments: Vec, // Table column alignments
pub list_stack: Vec, // Nested list tracking
pub link_stack: Vec, // Nested link tracking
pub heading_stack: Vec, // Header ID tracking
pub currently_in_code_block: bool, // Code block state
pub currently_in_footnote: bool, // Footnote state
}
```
## Event Handling
The renderer processes various Markdown events:
```rust
impl> HtmlRenderer {
pub fn run<'a, I>(&mut self, iter: I) -> Result<()>
where
I: Iterator- >,
{
let mut iter = iter.peekable();
while let Some(event) = iter.next() {
match event {
Event::Start(tag) => self.handle_start(&mut iter, tag)?,
Event::End(tag) => self.handle_end(tag)?,
Event::Text(text) => self.writer.text(&text)?,
Event::Code(text) => self.handle_inline_code(&text)?,
Event::Html(html) => self.writer.write_str(&html)?,
Event::SoftBreak => self.writer.soft_break()?,
Event::HardBreak => self.writer.hard_break()?,
Event::Rule => self.writer.horizontal_rule()?,
Event::FootnoteReference(name) => self.writer.footnote_reference(&name)?,
Event::TaskListMarker(checked) => self.writer.task_list_item(checked)?,
Event::InlineMath(_) | Event::DisplayMath(_) | Event::InlineHtml(_) => todo!(),
}
}
Ok(())
}
}
```
## Element Handling Examples
### Headers
```rust
// Input Markdown
# Level 1
## Level 2
// Generated HTML
Level 1
Level 2
```
### Lists
```rust
// Input Markdown
1. First
2. Second
* Nested
* Items
// Generated HTML
- First
- Second
```
### Tables
```rust
// Input Markdown
| Left | Center | Right |
|:-----|:------:|------:|
| 1 | 2 | 3 |
// Generated HTML
```
## Error Handling
The renderer uses a custom error type:
```rust
pub enum HtmlError {
Io(io::Error),
Write(fmt::Error),
Theme(String),
Config(String),
Render(String),
}
```
Handle errors appropriately:
```rust
fn render_markdown(markdown: &str) -> Result {
let config = HtmlConfig::default();
let parser = Parser::new(markdown);
let mut output = String::new();
push_html(&mut output, parser, &config)?;
Ok(output)
}
```
## Writing Output
The library supports different output methods:
```rust
// String output
let mut output = String::new();
push_html(&mut output, parser, &config)?;
// Write to formatter
write_html_fmt(&mut output, parser, &config)?;
// Write to IO
let file = File::create("output.html")?;
write_html_io(file, parser, &config)?;
```
## Best Practices
1. **State Management**
- Reset state between documents
- Check state before operations
- Handle nested structures carefully
2. **Error Handling**
- Always handle potential errors
- Use appropriate error variants
- Provide meaningful error messages
3. **Performance**
- Create Parser instances appropriately
- Reuse writers when processing multiple documents
- Consider buffer size for large documents
4. **Memory Usage**
- Clear state between documents
- Be mindful of string allocations
- Handle large documents efficiently
## Next Steps
- Learn about [Syntax Highlighting](syntax-highlighting.md)
- Implement [Custom Writers](custom-writers.md)
- See [Examples](../examples/basic-usage.md)