Variables & Params
Store, reuse, and pass data throughout your PDF templates using parameters and calculated values.
Learning Objectives
By the end of this article, you’ll be able to:
- Set and use document parameters via
doc.Params - Pass data from C# to templates
- Create calculated values and reuse them
- Understand parameter scope and lifetime
- Use global vs local parameters
- Organize complex data structures
Document Parameters (doc.Params)
The doc.Params dictionary is the primary way to pass data from C# to your templates.
Basic Usage
var doc = Document.ParseDocument("template.html");
// Set a single parameter
doc.Params["title"] = "Monthly Report";
// Set multiple parameters
doc.Params["author"] = "John Doe";
doc.Params["date"] = DateTime.Now;
doc.Params["pageCount"] = 10;
doc.SaveAsPDF("output.pdf");
Template:
<h1>{{title}}</h1>
<p>Author: {{author}}</p>
<p>Date: {{format(date, 'yyyy-MM-dd')}}</p>
<p>Pages: {{pageCount}}</p>
Passing Complex Objects
Anonymous Objects
doc.Params["model"] = new
{
customerName = "Acme Corporation",
invoiceNumber = "INV-2025-001",
date = DateTime.Now,
items = new[]
{
new { description = "Widget A", quantity = 5, price = 10.00 },
new { description = "Widget B", quantity = 3, price = 15.00 }
},
total = 95.00
};
Template:
<h1>Invoice #{{model.invoiceNumber}}</h1>
<p>Customer: {{model.customerName}}</p>
<p>Date: {{format(model.date, 'MMMM dd, yyyy')}}</p>
<table>
{{#each model.items}}
<tr>
<td>{{this.description}}</td>
<td>{{this.quantity}}</td>
<td>${{this.price}}</td>
</tr>
{{/each}}
</table>
<p><strong>Total: ${{model.total}}</strong></p>
Typed Classes
public class Invoice
{
public string InvoiceNumber { get; set; }
public string CustomerName { get; set; }
public DateTime Date { get; set; }
public List<InvoiceItem> Items { get; set; }
public decimal Total { get; set; }
}
public class InvoiceItem
{
public string Description { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
}
// Usage
var invoice = new Invoice
{
InvoiceNumber = "INV-2025-001",
CustomerName = "Acme Corporation",
Date = DateTime.Now,
Items = new List<InvoiceItem>
{
new InvoiceItem { Description = "Widget A", Quantity = 5, Price = 10.00m },
new InvoiceItem { Description = "Widget B", Quantity = 3, Price = 15.00m }
},
Total = 95.00m
};
doc.Params["model"] = invoice;
Multiple Parameters
You can set multiple independent parameters:
// Company info
doc.Params["company"] = new
{
name = "Tech Corp",
address = "123 Main St",
phone = "555-1234"
};
// Report data
doc.Params["report"] = new
{
title = "Q4 Sales",
period = "2024 Q4",
data = salesData
};
// User info
doc.Params["currentUser"] = new
{
name = "John Doe",
role = "Manager"
};
// Configuration
doc.Params["config"] = new
{
showHeader = true,
showFooter = true,
includeCharts = false
};
Template:
<div class="header">
<h1>{{company.name}}</h1>
<p>{{company.address}} | {{company.phone}}</p>
</div>
<h2>{{report.title}}</h2>
<p>Period: {{report.period}}</p>
<p>Generated by: {{currentUser.name}} ({{currentUser.role}})</p>
{{#if config.showHeader}}
<!-- Header content -->
{{/if}}
Calculated Values
Pre-calculate values in C# and pass them to templates:
var items = new[]
{
new { description = "Item 1", quantity = 5, price = 10.00 },
new { description = "Item 2", quantity = 3, price = 15.00 },
new { description = "Item 3", quantity = 2, price = 20.00 }
};
// Calculate totals
var subtotal = items.Sum(i => i.quantity * i.price);
var tax = subtotal * 0.08m;
var total = subtotal + tax;
doc.Params["model"] = new
{
items = items,
subtotal = subtotal,
tax = tax,
total = total,
taxRate = 0.08,
itemCount = items.Length
};
Template:
<p>Items: {{model.itemCount}}</p>
<p>Subtotal: ${{format(model.subtotal, 'F2')}}</p>
<p>Tax ({{format(model.taxRate, 'P0')}}): ${{format(model.tax, 'F2')}}</p>
<p><strong>Total: ${{format(model.total, 'F2')}}</strong></p>
Why calculate in C#?
- More reliable than template calculations
- Better performance
- Easier to unit test
- Type-safe calculations
- Access to full .NET capabilities
Parameter Scope
Global Parameters
Parameters set via doc.Params are available throughout the entire document:
doc.Params["companyName"] = "Tech Corp";
doc.Params["year"] = 2025;
<!-- Available in header -->
<div class="header">
<h1>{{companyName}}</h1>
</div>
<!-- Available in body -->
<div class="content">
<p>Copyright {{year}} {{companyName}}</p>
</div>
<!-- Available in footer -->
<div class="footer">
<p>{{companyName}} - {{year}}</p>
</div>
Common Patterns
Pattern 1: Separating Data and Metadata
// Content data
doc.Params["data"] = actualData;
// Metadata
doc.Params["meta"] = new
{
generatedDate = DateTime.Now,
generatedBy = currentUser.Name,
version = "1.0",
documentType = "Invoice"
};
// Configuration
doc.Params["config"] = new
{
showWatermark = false,
includePageNumbers = true,
colorScheme = "blue"
};
Pattern 2: Passing Helper Values
doc.Params["model"] = invoiceData;
// Helper values for calculations and formatting
doc.Params["helpers"] = new
{
currentDate = DateTime.Now,
currencySymbol = "$",
dateFormat = "MMMM dd, yyyy",
numberFormat = "F2",
taxRate = 0.08
};
Template:
<p>Generated: {{format(helpers.currentDate, helpers.dateFormat)}}</p>
<p>Tax Rate: {{format(helpers.taxRate, 'P0')}}</p>
<p>Total: {{helpers.currencySymbol}}{{format(model.total, helpers.numberFormat)}}</p>
Pattern 3: Conditional Configuration
var reportType = "detailed"; // or "summary"
doc.Params["model"] = reportData;
doc.Params["config"] = new
{
reportType = reportType,
showDetails = reportType == "detailed",
showCharts = reportType == "detailed",
showSummaryOnly = reportType == "summary"
};
Template:
<h1>{{model.title}}</h1>
{{#if config.showSummaryOnly}}
<div class="summary">
<p>{{model.summary}}</p>
</div>
{{/if}}
{{#if config.showDetails}}
<div class="details">
<h2>Detailed Analysis</h2>
<!-- Detailed content -->
</div>
{{/if}}
{{#if config.showCharts}}
<div class="charts">
<!-- Chart content -->
</div>
{{/if}}
Practical Examples
Example 1: Multi-Section Report with Shared Data
C# Code:
var doc = Document.ParseDocument("report.html");
// Global company info
doc.Params["company"] = new
{
name = "Tech Corp",
logo = "./images/logo.png",
address = "123 Main St, Springfield, IL",
phone = "555-1234",
email = "info@techcorp.com"
};
// Report metadata
doc.Params["report"] = new
{
title = "Annual Financial Report",
year = 2024,
preparedBy = "Finance Department",
preparedDate = DateTime.Now,
confidential = true
};
// Financial data
doc.Params["financials"] = new
{
revenue = 5000000,
expenses = 3500000,
profit = 1500000,
profitMargin = 0.30,
yearOverYearGrowth = 0.15
};
// Quarterly breakdown
doc.Params["quarters"] = new[]
{
new { quarter = "Q1", revenue = 1100000, expenses = 850000 },
new { quarter = "Q2", revenue = 1200000, expenses = 900000 },
new { quarter = "Q3", revenue = 1300000, expenses = 875000 },
new { quarter = "Q4", revenue = 1400000, expenses = 875000 }
};
doc.SaveAsPDF("annual-report.pdf");
Template:
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>{{report.title}}</title>
<style>
@page {
size: Letter;
margin: 1in;
@top-center {
content: "{{company.name}} - {{report.year}}";
font-size: 9pt;
color: #666;
}
@bottom-center {
content: "Page " counter(page) " of " counter(pages);
font-size: 9pt;
color: #666;
}
}
body {
font-family: Helvetica, sans-serif;
}
h1 {
color: #1e40af;
border-bottom: 3pt solid #1e40af;
padding-bottom: 10pt;
}
.confidential {
background-color: #fee2e2;
color: #991b1b;
padding: 10pt;
border: 2pt solid #dc2626;
font-weight: bold;
text-align: center;
margin-bottom: 20pt;
}
.metric-box {
display: inline-block;
width: 30%;
padding: 15pt;
margin: 10pt 1%;
border: 2pt solid #2563eb;
text-align: center;
border-radius: 5pt;
}
.metric-value {
font-size: 24pt;
font-weight: bold;
color: #2563eb;
}
.metric-label {
font-size: 10pt;
color: #666;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20pt 0;
}
th {
background-color: #2563eb;
color: white;
padding: 10pt;
text-align: left;
}
td {
padding: 8pt;
border-bottom: 1pt solid #e5e7eb;
}
</style>
</head>
<body>
<!-- Header with company info -->
<div style="text-align: center; margin-bottom: 30pt;">
<img src="{{company.logo}}" style="height: 60pt;" />
<h1>{{report.title}}</h1>
<p style="font-size: 12pt;">{{report.year}}</p>
</div>
{{#if report.confidential}}
<div class="confidential">
CONFIDENTIAL - Internal Use Only
</div>
{{/if}}
<!-- Report metadata -->
<p style="font-size: 10pt; color: #666;">
<strong>Prepared by:</strong> {{report.preparedBy}}<br />
<strong>Date:</strong> {{format(report.preparedDate, 'MMMM dd, yyyy')}}<br />
<strong>Company:</strong> {{company.name}}
</p>
<!-- Key metrics -->
<h2>Financial Summary</h2>
<div>
<div class="metric-box">
<div class="metric-value">{{format(financials.revenue, 'C0')}}</div>
<div class="metric-label">Total Revenue</div>
</div>
<div class="metric-box">
<div class="metric-value">{{format(financials.profit, 'C0')}}</div>
<div class="metric-label">Net Profit</div>
</div>
<div class="metric-box">
<div class="metric-value">{{format(financials.profitMargin, 'P0')}}</div>
<div class="metric-label">Profit Margin</div>
</div>
</div>
<!-- Quarterly breakdown -->
<h2>Quarterly Performance</h2>
<table>
<thead>
<tr>
<th>Quarter</th>
<th style="text-align: right;">Revenue</th>
<th style="text-align: right;">Expenses</th>
<th style="text-align: right;">Profit</th>
<th style="text-align: right;">Margin</th>
</tr>
</thead>
<tbody>
{{#each quarters}}
<tr>
<td>{{this.quarter}}</td>
<td style="text-align: right;">{{format(this.revenue, 'C0')}}</td>
<td style="text-align: right;">{{format(this.expenses, 'C0')}}</td>
<td style="text-align: right;">
{{format(calc(this.revenue, '-', this.expenses), 'C0')}}
</td>
<td style="text-align: right;">
{{format(calc(calc(this.revenue, '-', this.expenses), '/', this.revenue), 'P0')}}
</td>
</tr>
{{/each}}
</tbody>
</table>
<!-- Footer with company contact -->
<div style="margin-top: 50pt; padding-top: 15pt; border-top: 1pt solid #d1d5db; font-size: 9pt; color: #666;">
<p>
<strong>{{company.name}}</strong><br />
{{company.address}}<br />
Phone: {{company.phone}} | Email: {{company.email}}
</p>
</div>
</body>
</html>
Example 2: Reusable Template Components
C# Code:
// Create reusable styling configuration
var brandColors = new
{
primary = "#2563eb",
secondary = "#3b82f6",
success = "#059669",
warning = "#f59e0b",
danger = "#dc2626",
textDark = "#1f2937",
textLight = "#6b7280",
backgroundLight = "#f9fafb"
};
var typography = new
{
fontFamily = "Helvetica, sans-serif",
h1Size = "24pt",
h2Size = "20pt",
h3Size = "16pt",
bodySize = "11pt",
smallSize = "9pt"
};
var layout = new
{
pageSize = "Letter",
margin = "1in",
contentWidth = "6.5in",
sidebarWidth = "2in"
};
doc.Params["brand"] = brandColors;
doc.Params["typography"] = typography;
doc.Params["layout"] = layout;
doc.Params["model"] = actualData;
Template:
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>Document</title>
<style>
@page {
size: {{layout.pageSize}};
margin: {{layout.margin}};
}
body {
font-family: {{typography.fontFamily}};
font-size: {{typography.bodySize}};
color: {{brand.textDark}};
}
h1 {
font-size: {{typography.h1Size}};
color: {{brand.primary}};
}
h2 {
font-size: {{typography.h2Size}};
color: {{brand.secondary}};
}
h3 {
font-size: {{typography.h3Size}};
color: {{brand.textDark}};
}
.success {
color: {{brand.success}};
}
.warning {
color: {{brand.warning}};
}
.danger {
color: {{brand.danger}};
}
.muted {
color: {{brand.textLight}};
}
</style>
</head>
<body>
<h1>{{model.title}}</h1>
<!-- Content uses brand colors -->
</body>
</html>
Try It Yourself
Exercise 1: Configuration-Driven Template
Create a template that:
- Accepts a configuration object controlling layout options
- Shows/hides sections based on config
- Uses config for styling choices (colors, fonts)
- Accepts separate data object for content
Exercise 2: Multi-Page Report
Create a report that:
- Uses global company parameters for header/footer
- Accepts multiple data parameters (sales, customers, inventory)
- Calculates summary values in C#
- Passes formatting preferences as parameters
Exercise 3: Template Factory
Create a C# method that:
- Accepts different report types
- Sets appropriate parameters based on type
- Passes different data based on type
- Returns generated PDF
Common Pitfalls
❌ Modifying Parameters After Parsing
var doc = Document.ParseDocument("template.html");
doc.SaveAsPDF("output.pdf");
doc.Params["model"] = data; // Too late!
✅ Solution: Set parameters before saving
var doc = Document.ParseDocument("template.html");
doc.Params["model"] = data; // Set first
doc.SaveAsPDF("output.pdf");
❌ Overwriting Parameters
doc.Params["model"] = customerData;
doc.Params["model"] = invoiceData; // Overwrites customer data!
✅ Solution: Use different keys
doc.Params["customer"] = customerData;
doc.Params["invoice"] = invoiceData;
❌ Not Null-Checking Data
doc.Params["model"] = null; // Will cause issues in template
✅ Solution: Validate data
if (data != null)
{
doc.Params["model"] = data;
}
else
{
doc.Params["model"] = new { /* default values */ };
}
Next Steps
Now that you understand parameters:
- Context & Scope - Master data access in nested contexts
- Formatting Output - Format dates, numbers, and text
- Advanced Patterns - Complex data binding scenarios
Continue learning → Context & Scope