Skip to content

Commit 190b20b

Browse files
author
Tom Softreck
committed
update docs and first draft
1 parent 3dfe71a commit 190b20b

File tree

8 files changed

+1089
-0
lines changed

8 files changed

+1089
-0
lines changed

examples/invoice/README.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Redoc Invoice Example
2+
3+
This example demonstrates how to use Redoc's template system to generate professional invoices with bidirectional document conversion capabilities.
4+
5+
## Features
6+
7+
- Generate PDF and HTML invoices from JSON data
8+
- Customizable invoice template using HTML/CSS
9+
- Extract structured data from existing invoices
10+
- Convert between different document formats (PDF, HTML, JSON)
11+
- Support for multiple line items, taxes, and discounts
12+
- Professional styling with responsive design
13+
14+
## Prerequisites
15+
16+
- Python 3.9+
17+
- Redoc package installed
18+
- Required dependencies (will be installed automatically with Redoc)
19+
20+
## Installation
21+
22+
1. Clone the Redoc repository:
23+
```bash
24+
git clone https://github.com/text2doc/redoc.git
25+
cd redoc
26+
```
27+
28+
2. Install the package in development mode:
29+
```bash
30+
pip install -e .
31+
```
32+
33+
3. Install additional development dependencies:
34+
```bash
35+
pip install -e ".[dev]"
36+
```
37+
38+
## Directory Structure
39+
40+
```
41+
invoice/
42+
├── README.md # This file
43+
├── demo.py # Demo script
44+
├── data/
45+
│ └── sample_invoice.json # Sample invoice data
46+
├── templates/
47+
│ └── invoice.html # Invoice HTML template
48+
└── output/ # Generated files will be saved here
49+
```
50+
51+
## Usage
52+
53+
### 1. Generate an Invoice
54+
55+
Run the demo script to generate a PDF invoice from the sample data:
56+
57+
```bash
58+
python examples/invoice/demo.py
59+
```
60+
61+
This will:
62+
1. Load the sample invoice data from `data/sample_invoice.json`
63+
2. Generate a PDF invoice in the `output/` directory
64+
3. Create an HTML version of the invoice
65+
4. Attempt to extract data from the generated PDF
66+
5. Demonstrate document format conversion
67+
68+
### 2. Customize the Template
69+
70+
Edit the template file at `templates/invoice.html` to customize the invoice design. The template uses:
71+
72+
- **Jinja2** templating syntax
73+
- **CSS** for styling
74+
- **Responsive design** that works on different screen sizes
75+
76+
### 3. Use Your Own Data
77+
78+
1. Create a JSON file with your invoice data following the same structure as `sample_invoice.json`
79+
2. Update the `demo.py` script to use your data file
80+
3. Run the script to generate your custom invoice
81+
82+
### 4. Extract Data from Existing Invoices
83+
84+
The example includes a basic implementation for extracting data from PDF invoices. To use it:
85+
86+
```python
87+
from redoc.templates.pdf_handler import PDFTemplateHandler
88+
89+
handler = PDFTemplateHandler()
90+
extracted_data = handler.extract_data("path/to/your/invoice.pdf")
91+
print(extracted_data)
92+
```
93+
94+
## Template Variables
95+
96+
The invoice template expects the following variables:
97+
98+
### Required Variables
99+
- `invoice_number`: Unique invoice identifier
100+
- `issue_date`: Date the invoice was issued
101+
- `due_date`: Payment due date
102+
- `company`: Dictionary with company details (name, address, etc.)
103+
- `client`: Dictionary with client details
104+
- `items`: List of invoice line items
105+
106+
### Optional Variables
107+
- `status`: Invoice status (draft, sent, paid, etc.)
108+
- `currency`: Currency symbol (default: '$')
109+
- `notes`: Additional notes
110+
- `terms`: Payment terms
111+
- `discount`: Discount amount
112+
- `payment_instructions`: Instructions for payment
113+
- `bank_info`: Bank account details
114+
115+
## Extending the Example
116+
117+
### Add New Templates
118+
1. Create a new HTML template in the `templates/` directory
119+
2. Update the demo script to use your new template
120+
121+
### Support Additional Formats
122+
1. Create a new template handler class (similar to `PDFTemplateHandler`)
123+
2. Implement the required methods for your format
124+
3. Update the demo script to use your new handler
125+
126+
## License
127+
128+
This example is part of the Redoc project and is licensed under the Apache 2.0 License.
129+
130+
## Contributing
131+
132+
Contributions are welcome! Please feel free to submit a Pull Request.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"invoice_number": "INV-2023-001",
3+
"issue_date": "2023-11-15",
4+
"due_date": "2023-12-15",
5+
"status": "sent",
6+
"currency": "USD",
7+
"notes": "Thank you for your business. Please make payment by the due date.",
8+
"terms": "Payment due within 30 days of issue date.",
9+
10+
"company": {
11+
"name": "Acme Corporation",
12+
"address": [
13+
"123 Business Rd",
14+
"San Francisco, CA 94107",
15+
"United States"
16+
],
17+
"phone": "+1 (555) 123-4567",
18+
"email": "billing@acmecorp.com",
19+
"website": "www.acmecorp.com"
20+
},
21+
22+
"client": {
23+
"name": "Tech Solutions Inc.",
24+
"address": [
25+
"456 Client Ave",
26+
"New York, NY 10001",
27+
"United States"
28+
],
29+
"phone": "+1 (555) 987-6543",
30+
"email": "accounts@techsolutions.com"
31+
},
32+
33+
"items": [
34+
{
35+
"description": "Web Development Services",
36+
"quantity": 40,
37+
"unit_price": 75.00,
38+
"tax_rate": 0.20
39+
},
40+
{
41+
"description": "UI/UX Design",
42+
"quantity": 25,
43+
"unit_price": 95.00,
44+
"tax_rate": 0.20
45+
},
46+
{
47+
"description": "Project Management",
48+
"quantity": 15,
49+
"unit_price": 120.00,
50+
"tax_rate": 0.20
51+
}
52+
],
53+
54+
"discount": 150.00,
55+
"payment_instructions": "Please include the invoice number in your payment reference.",
56+
"bank_info": {
57+
"bank_name": "First National Bank",
58+
"account_name": "Acme Corporation Inc.",
59+
"account_number": "1234567890",
60+
"routing_number": "021000021",
61+
"swift_code": "FNBOUS44",
62+
"iban": "US6005400000000000000000"
63+
}
64+
}

examples/invoice/demo.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
"""
2+
Redoc Invoice Template Demo
3+
4+
This script demonstrates bidirectional document conversion using Redoc's template system.
5+
It shows how to:
6+
1. Generate a PDF invoice from a JSON data file and HTML template
7+
2. Extract data from an existing PDF invoice
8+
3. Convert between different document formats
9+
"""
10+
11+
import json
12+
import sys
13+
from pathlib import Path
14+
from typing import Dict, Any, Optional
15+
16+
# Add the project root to the Python path
17+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
18+
19+
from redoc.templates.pdf_handler import PDFTemplateHandler
20+
from redoc.templates.base import TemplateError
21+
22+
# Paths
23+
TEMPLATE_DIR = Path(__file__).parent / "templates"
24+
DATA_DIR = Path(__file__).parent / "data"
25+
OUTPUT_DIR = Path(__file__).parent / "output"
26+
27+
# Ensure output directory exists
28+
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
29+
30+
def load_json_data(file_path: Path) -> Dict[str, Any]:
31+
"""Load JSON data from a file."""
32+
try:
33+
with open(file_path, 'r', encoding='utf-8') as f:
34+
return json.load(f)
35+
except Exception as e:
36+
print(f"Error loading JSON file {file_path}: {e}")
37+
sys.exit(1)
38+
39+
def generate_invoice() -> None:
40+
"""Generate a PDF invoice from a template and data file."""
41+
print("\n=== Generating Invoice ===")
42+
43+
# Load invoice data
44+
data_file = DATA_DIR / "sample_invoice.json"
45+
print(f"Loading data from {data_file}")
46+
invoice_data = load_json_data(data_file)
47+
48+
# Initialize PDF template handler
49+
handler = PDFTemplateHandler(template_dir=str(TEMPLATE_DIR))
50+
51+
# Generate PDF
52+
output_pdf = OUTPUT_DIR / "generated_invoice.pdf"
53+
print(f"Generating PDF: {output_pdf}")
54+
55+
try:
56+
# Add calculated fields to the data
57+
subtotal = sum(item['quantity'] * item['unit_price'] for item in invoice_data['items'])
58+
tax_amount = sum(item['quantity'] * item['unit_price'] * item['tax_rate']
59+
for item in invoice_data['items'])
60+
total = subtotal + tax_amount - invoice_data.get('discount', 0)
61+
62+
invoice_data.update({
63+
'subtotal': subtotal,
64+
'tax_amount': tax_amount,
65+
'total': total
66+
})
67+
68+
# Render the template and generate PDF
69+
handler.render_pdf(
70+
template_name="invoice.html",
71+
data=invoice_data,
72+
output_pdf=str(output_pdf)
73+
)
74+
print(f"Successfully generated: {output_pdf}")
75+
76+
# Also generate HTML for reference
77+
html_output = OUTPUT_DIR / "generated_invoice.html"
78+
with open(html_output, 'w', encoding='utf-8') as f:
79+
template = handler.renderer.get_template("invoice.html")
80+
html_content = template.render(**invoice_data)
81+
f.write(html_content)
82+
print(f"Generated HTML version: {html_output}")
83+
84+
return output_pdf
85+
86+
except TemplateError as e:
87+
print(f"Error generating invoice: {e}")
88+
sys.exit(1)
89+
90+
def extract_invoice_data(pdf_path: Path) -> Dict[str, Any]:
91+
"""Extract structured data from a PDF invoice."""
92+
print("\n=== Extracting Data from Invoice ===")
93+
print(f"Processing PDF: {pdf_path}")
94+
95+
if not pdf_path.exists():
96+
print(f"Error: File not found: {pdf_path}")
97+
return {}
98+
99+
try:
100+
handler = PDFTemplateHandler()
101+
extracted_data = handler.extract_data(str(pdf_path))
102+
103+
# Save extracted data to JSON
104+
output_json = OUTPUT_DIR / "extracted_invoice_data.json"
105+
with open(output_json, 'w', encoding='utf-8') as f:
106+
json.dump(extracted_data, f, indent=2)
107+
108+
print(f"Extracted data saved to: {output_json}")
109+
return extracted_data
110+
111+
except Exception as e:
112+
print(f"Error extracting data from PDF: {e}")
113+
return {}
114+
115+
def convert_document(
116+
input_path: Path,
117+
output_path: Path,
118+
from_format: str = 'pdf',
119+
to_format: str = 'html'
120+
) -> None:
121+
"""Convert a document between different formats."""
122+
print(f"\n=== Converting {from_format.upper()} to {to_format.upper()} ===")
123+
print(f"Input: {input_path}")
124+
print(f"Output: {output_path}")
125+
126+
if not input_path.exists():
127+
print(f"Error: Input file not found: {input_path}")
128+
return
129+
130+
try:
131+
# In a real implementation, you would use the appropriate converter
132+
# based on the input and output formats
133+
if from_format == 'pdf' and to_format == 'html':
134+
# Simple conversion using pdf2htmlEX or similar
135+
# This is a placeholder - you'd need to implement the actual conversion
136+
print(f"Converting {input_path} to HTML (simulated)")
137+
with open(input_path, 'rb') as f_in, open(output_path, 'w', encoding='utf-8') as f_out:
138+
f_out.write(f"<!-- Converted from {input_path.name} -->\n")
139+
f_out.write("<html><body>")
140+
f_out.write("<h1>Converted Document</h1>")
141+
f_out.write(f"<p>This is a simulated conversion from {from_format} to {to_format}.</p>")
142+
f_out.write("</body></html>")
143+
else:
144+
print(f"Conversion from {from_format} to {to_format} not implemented in this demo.")
145+
return
146+
147+
print(f"Successfully converted to: {output_path}")
148+
149+
except Exception as e:
150+
print(f"Error during conversion: {e}")
151+
152+
def main():
153+
"""Run the demo script."""
154+
print("=" * 50)
155+
print("Redoc Document Conversion Demo")
156+
print("=" * 50)
157+
158+
# 1. Generate a PDF invoice from template and data
159+
pdf_path = generate_invoice()
160+
161+
# 2. Extract data from the generated PDF
162+
if pdf_path and pdf_path.exists():
163+
extracted_data = extract_invoice_data(pdf_path)
164+
165+
# 3. Demonstrate document conversion
166+
if extracted_data:
167+
# Convert the extracted data to a different format
168+
output_html = OUTPUT_DIR / "converted_invoice.html"
169+
convert_document(pdf_path, output_html, 'pdf', 'html')
170+
171+
print("\nDemo completed!")
172+
print(f"Check the '{OUTPUT_DIR}' directory for generated files.")
173+
174+
if __name__ == "__main__":
175+
main()

0 commit comments

Comments
 (0)